Using FTD2XX_NET managed .NET wrapper class to read real time temperature data

coleg picture coleg · Sep 19, 2013 · Viewed 7.6k times · Source

I have a problem reading real time temperature data from the DS18B20+ sensor on a DLPIO20 device. I'm using the FTD2XX.DLL .NET wrapper (version 1.0.14) on the Windows platform (as per link: http://www.ftdichip.com/Support/SoftwareExamples/CodeExamples/CSharp/FTD2XX_NET_v1.0.14.zip).

Test application to demonstrate the issue is below.

Here is what I mean by real-time.

2.1 At fairly constant ambient temperature (let's say 20° C) have few temperature reads to see consistent results in debug mode (break point at the end of temperature read cycle on line 55).

2.2.Then, while on break point, just breath on sensor or use other means to heat it few degrees above ambient.

2.3. Run two or three more cycles of temperature readings.

The first read I get after the sensor heats up is the 'old' temperature of 20° C. Only on second or third read cycles I've got realistic results around 28° C.

What's interesting, with test application program IO20Demo (provided with the purchase of the DLP-IO20 board) if I issued Convert command before the read, I get a real-time result of 28° C right away. With this program and managed .NET wrapper class I also send Convert command before Read, but to no avail. The big difference is IO20Demo doesn't use managed wrapper class, but uses FTD2XX.lib directly.

Could you help me understand what I'm doing wrong here? How I can get real-time temperature data using managed .NET wrapper class?

Your help is greatly appreciated!

/// <summary>
/// Program for DLPIO20 device to read temperature data from DS18B20+
/// digital temperature sensor attached to one of its channels. 
/// </summary>
class Program
{
    static void Main(string[] args)
    {
        FTDI FtdiWrapper = null;

        // DLPIO20 channel where the DS18B20+ sensor is attached
        byte sensorChannel = 0x06; 

        try
        {
            // create new instance of the FTDI device class
            FtdiWrapper = ConnectToFirstFtdiDevice();

            if (FtdiWrapper == null || !FtdiWrapper.IsOpen)
            {
                throw new Exception("Error connection to FTDI device.");
            }

            // didn't helped at all for 85dC issue
            //PurgeRxBuffer(FtdiWrapper);

            // helped avoid 85dC issue at first read 
            ConvertSensorData(FtdiWrapper, sensorChannel);

            // send read sensor command
            float? degreesC = null;
            for (int i = 0; i < 100; i++)
            {
                // calling Convert just before ReadTemperatureSensor causes
                // IO20Demo (using FTD2XX.lib) to return real temperature
                // but it doesn't help when using .NET wrapper
                ConvertSensorData(FtdiWrapper, sensorChannel);

                // other failed attempts to get real time sensor data

                // previous value returned:
                //PurgeRxBuffer(FtdiWrapper);

                // read but only initiate conversion on success
                degreesC = ReadTemperatureSensor(FtdiWrapper, sensorChannel);

                if (degreesC == null)
                {
                    throw new Exception("Error converting raw data to Celsius");
                }

                var message = string.Format("Success! {0}° Celsius.", degreesC);
                Console.WriteLine(message);
            }

            Console.WriteLine("Press any key to exit ...");
            Console.ReadKey();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            Console.WriteLine("Press any key to exit ...");
            Console.ReadKey();
        }
        finally
        {
            if (FtdiWrapper != null && FtdiWrapper.IsOpen)
            {
                FtdiWrapper.Close();
            }
        }
    }

This is the code used to connect to the first FTDI device

    static private FTDI ConnectToFirstFtdiDevice()
    {
        FTDI FtdiWrapper = new FTDI();
        UInt32 ftdiDeviceCount = 0;
        FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OTHER_ERROR;

        // Determine the number of FTDI devices connected to the machine
        ftStatus = FtdiWrapper.GetNumberOfDevices(ref ftdiDeviceCount);
        // Check status
        if (ftStatus != FTDI.FT_STATUS.FT_OK)
            throw new Exception(string.Format("Error after GetNumberOfDevices(), ftStatus: {0}", ftStatus));

        if (ftdiDeviceCount == 0)
            throw new Exception("No FTDI device found");

        // Allocate storage for device info list
        var ftdiDeviceList = new FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];

        // Populate our device list
        ftStatus = FtdiWrapper.GetDeviceList(ftdiDeviceList);

        if (ftStatus == FTDI.FT_STATUS.FT_OK)
        {
            // Open first device in our list by serial number
            ftStatus = FtdiWrapper.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
                throw new Exception(string.Format("Error opening first device, ftStatus: {0}", ftStatus));

            ftStatus = FtdiWrapper.SetBaudRate(9600);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
                throw new Exception(string.Format("Error to set Baud rate, ftStatus: {0}", ftStatus));

            // Set data characteristics - Data bits, Stop bits, Parity
            ftStatus = FtdiWrapper.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8, FTDI.FT_STOP_BITS.FT_STOP_BITS_1, FTDI.FT_PARITY.FT_PARITY_NONE);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
                throw new Exception(string.Format("Error to set data characteristics , ftStatus: {0}", ftStatus));

            // Set flow control - set RTS/CTS flow control
            ftStatus = FtdiWrapper.SetFlowControl(FTDI.FT_FLOW_CONTROL.FT_FLOW_RTS_CTS, 0x11, 0x13);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
                throw new Exception(string.Format("Error to set flow control , ftStatus: {0}", ftStatus));

            // Set read timeout to 5 seconds, write timeout to infinite
            ftStatus = FtdiWrapper.SetTimeouts(5000, 0);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
                throw new Exception(string.Format("Error to set timeouts, ftStatus: {0}", ftStatus));

        }

        return FtdiWrapper;
    }

Method to convert raw sensor data

    static private void ConvertSensorData(FTDI FtdiWrapper, byte sensorChannel)
    {
        FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OTHER_ERROR;
        UInt32 numBytesWritten = 0;

        byte[] convertCommand = new byte[] { 0x03, 0x40, sensorChannel };
        ftStatus = FtdiWrapper.Write(convertCommand, convertCommand.Length, ref numBytesWritten);

        bool isAllBytesWritten = numBytesWritten == convertCommand.Length;
        if (ftStatus != FTDI.FT_STATUS.FT_OK && isAllBytesWritten)
            throw new Exception(string.Format("Failed to write Convert command to device; Status: {0},  isAllBytesWritten: {1}", ftStatus.ToString(), isAllBytesWritten));
    }

Method to read sensor temperature

    static private float? ReadTemperatureSensor(FTDI FtdiWrapper, byte sensorChannel)
    {
        float? degreesC = null;
        FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OTHER_ERROR;
        UInt32 numBytesWritten = 0;

        byte[] readSensorCommand = new byte[] { 0x03, 0x41, sensorChannel };
        ftStatus = FtdiWrapper.Write(readSensorCommand, readSensorCommand.Length, ref numBytesWritten);

        bool isAllBytesWritten = numBytesWritten == readSensorCommand.Length;
        if (ftStatus != FTDI.FT_STATUS.FT_OK && isAllBytesWritten)
            throw new Exception(string.Format("Failed to write readSensorCommand to device; Status: {0},  isAllBytesWritten: {1}", ftStatus.ToString(), isAllBytesWritten));

        // Read back response
        UInt32 numBytesAvailable = 0;
        UInt32 numBytesExpected = 2;    // read sensor command expected to return 2 bytes
        while (numBytesAvailable == 0)
        {
            Thread.Sleep(40); // value of 40 taken from DLP IO20 demo solution
            ftStatus = FtdiWrapper.GetRxBytesAvailable(ref numBytesAvailable);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
                throw new Exception("Failed to get number of bytes available to read; error: " + ftStatus);

        } //while (numBytesAvailable < numBytesExpected);

        if (numBytesAvailable != numBytesExpected)
            throw new Exception("Error: Invalid data in buffer. (1350)");

        UInt32 numBytesRead = 0;
        byte[] rawData = new byte[numBytesExpected];
        ftStatus = FtdiWrapper.Read(rawData, numBytesAvailable, ref numBytesRead);
        if (ftStatus != FTDI.FT_STATUS.FT_OK)
            throw new Exception("Failed to read data from device after command has been sent; error: " + ftStatus);

        //convert raw response data to degrees Celsius
        degreesC = ConvertTemperature(rawData);
        return degreesC;
    }

Method to purge the transmit buffer

    static private void PurgeRxBuffer(FTDI FtdiWrapper)
    {
        UInt32 numBytesAvailable = 0;
        UInt32 numBytesRead = 0;
        FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OTHER_ERROR;
        byte[] rx = new byte[1001]; //allocate large enough space to read from device

        Thread.Sleep(5);

        ftStatus = FtdiWrapper.GetRxBytesAvailable(ref numBytesAvailable);
        if (ftStatus != FTDI.FT_STATUS.FT_OK)
            throw new Exception("Failed to get number of bytes available to read; error: " + ftStatus);

        if (numBytesAvailable > 1000)
            numBytesAvailable = 1000;

        while (numBytesAvailable > 0)
        {
            //read the data from the buffer
            numBytesRead = 0;
            ftStatus = FtdiWrapper.Read(rx, numBytesAvailable, ref numBytesRead);


            ftStatus = FtdiWrapper.GetRxBytesAvailable(ref numBytesAvailable);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
                throw new Exception("Failed to get number of bytes available to read; error: " + ftStatus);

            if (numBytesAvailable > 1000)
                numBytesAvailable = 1000;

            Thread.Sleep(5);
        }
    }

Method to convert temperature from raw data to Celsius

    static private float? ConvertTemperature(byte[] rawData)
    {
        float? tempCelsius = null;
        bool isnegative = false;

        //check input
        if (rawData.Length < 2)
            throw new Exception(string.Format("Input parameter rawData for temperature conversion must be 2 bytes, actual length is: {0}", rawData.Length));

        int temp = rawData[0] | (rawData[1] << 8);
        if ((temp & 0x8000) == 0x8000)//if MSBit is set then negative temperature
        {
            temp &= 0x07ff;
            isnegative = true;
            temp = 0x800 - temp;
        }

        temp &= 0x07ff;
        tempCelsius = (float)((float)temp / 16.0);
        if (isnegative) tempCelsius *= -1;

        return tempCelsius;
    }
}

Answer

Jack Lewis picture Jack Lewis · Sep 6, 2014

I think you would need to set the latency timer on the FTDI device in your ConnectToFirstFtdiDevice to the min value, I use 16ms for mine and it helped solve this type of issue for me. The purgeRX is only a software buffer not HW so that will not prevent you from having stale measurements in the FTDI USB buffer.