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;
}
}
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.