Я в свое время делал так. Конечно это не идеальный код, но работает.
Главный плуг в этом коде - Sleep между запросом и ответом. В идеале интервалы должны устанавливаться структурой comtimeouts, но я почему-то так и не подобрал их. Еще одно: функции IEEE754.... нужны для приведения порядка байт в нужный (порядок байт в контроллере и компьютере могут отличатся). Если увидишь ситуацию, при которой ответ есть, но числа бредовые - скорее всего у тебя другой порядок байт.
//описание структур запроса и ответов 4,6 и 8 байт
union crc_union{
unsigned short integ;
BYTE bytes[2];
};
struct {
BYTE _1addr;
BYTE _2cmd;
BYTE _3data_start_addr_hi;
BYTE _4data_start_addr_lo;
BYTE _5data_length_hi;
BYTE _6data_length_lo;
BYTE _7crc1;
BYTE _8crc2;
} zapros;
struct otvet4{
BYTE _1from_addr;
BYTE _2cmd;
BYTE _3byte_count;
BYTE _4data[4];
BYTE _8crc1;
BYTE _9crc2;
} ;
struct otvet6{
BYTE _1from_addr;
BYTE _2cmd;
BYTE _3byte_count;
BYTE _4data[6];
BYTE _Acrc1;
BYTE _Bcrc2;
} ;
struct otvet8{
BYTE _1from_addr;
BYTE _2cmd;
BYTE _3byte_count;
BYTE _4data[8];
BYTE _Ccrc1;
BYTE _Dcrc2;
} ;
//псевдонимы функций
unsigned short __fastcall CRC16(unsigned char *Buffer, int Buffer_Length);
BOOL OpenCommPort(int Baudrate, int ByteSize, int Parity, int StopBits);
BOOL __fastcall floatNotNumber(BYTE src[4]);
float __fastcall IEEE754float(BYTE src[4]);
long __fastcall IEEE754long(BYTE src[4]);
double __fastcall IEEE754double(BYTE src[8]);
double ReadDouble(BYTE mbadr,BYTE addr_hi,BYTE addr_lo);
float ReadFloat(BYTE mbadr,BYTE addr_hi,BYTE addr_lo);
long ReadLong(BYTE mbadr,BYTE addr_hi,BYTE addr_lo);
//описание переменных
HANDLE Comport=NULL;
DCB dcb1;
COMMTIMEOUTS comtimeouts;
COMSTAT statistic;
DWORD nNumberOfBytesToWrite; // number of bytes to write
DWORD lpNumberOfBytesWritten; // pointer to number of bytes written
int IntEditTimeout=500;
BOOL BoolInvertedBytes=0;
AnsiString ComPort;
int IntBaudRate,IntParity,IntStopBits,IntByteSize;
BYTE MBadr;
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// вспомагательная функция вычисления контрольной суммы CRC16, используемой в ModbusRTU
unsigned short __fastcall CRC16(unsigned char *Buffer, int Buffer_Length)
{
//
// Algorithm - CRC16 Standard, Polynom - 8005 (X16 + X15 + X2 + 1)
// Init const = ffff
// Check: CRC16("123456789",9) = BB3D
//
const unsigned short CRC16_tbl[ 256 ] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 };
unsigned short CRC16 = 0xffff;
for (int i=0; i < Buffer_Length; i++)
{
CRC16 = ( CRC16 >> 8 ) ^ CRC16_tbl[ ( CRC16 & 0xFF ) ^ Buffer ];
}
return CRC16;
}
//-------------------------------------------------------------------------
//Функция открытия СОМ-порта
BOOL OpenCommPort(int baudrate, int bytesize, int parity, int stopbits)
{
Comport = CreateFile("COM1",
GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL );
if (Comport!=INVALID_HANDLE_VALUE)
{
GetCommState(Comport, &dcb1);
dcb1.BaudRate=baudrate;
dcb1.fBinary=1;
dcb1.fParity=1;
dcb1.fDtrControl=0;
dcb1.Parity=parity;
dcb1.ByteSize=bytesize;
dcb1.StopBits = stopbits;
dcb1.fOutxCtsFlow=0;
dcb1.fOutxDsrFlow=0;
dcb1.fDsrSensitivity=0;
dcb1.fOutX=0;
dcb1.fInX=0;
SetCommState(Comport, &dcb1);
comtimeouts.ReadIntervalTimeout=0;
comtimeouts.ReadTotalTimeoutMultiplier=0;
comtimeouts.WriteTotalTimeoutMultiplier=0;
comtimeouts.ReadTotalTimeoutConstant=500;
comtimeouts.WriteTotalTimeoutConstant=500;
SetCommTimeouts(&Comport,&comtimeouts);
return true;
}
else return false;
}
//--------------------------------------------------------------------------
//прочитать значение типа float из modbus-slave устройства с адресом mbadr, адрес регистров - addr_hi, addr_lo
float ReadFloat(BYTE mbadr,BYTE addr_hi,BYTE addr_lo)
{
crc_union crc_float, crc_otv_flt;
otvet4 otvet4f;
DWORD p;
crc_float.integ=0;
zapros._1addr=mbadr;
zapros._2cmd=0x03;
zapros._3data_start_addr_hi=addr_hi;
zapros._4data_start_addr_lo=addr_lo;
zapros._5data_length_hi=0x00;
zapros._6data_length_lo=0x02;
crc_float.integ=CRC16(&zapros._1addr,6);
zapros._7crc1=crc_float.bytes[0];
zapros._8crc2=crc_float.bytes[1];
for(p=0;p<3;p++)
{
ZeroMemory(&otvet4f,sizeof(otvet4f));
PurgeComm(Comport,PURGE_TXCLEAR);
PurgeComm(Comport,PURGE_RXCLEAR);
if (WriteFile(
Comport,
&zapros,
sizeof(zapros),
&lpNumberOfBytesWritten,
NULL
))
{
Sleep(IntEditTimeout);
ClearCommError(Comport,NULL,&statistic);
if (statistic.cbInQue>=sizeof(otvet4f))
ReadFile( Comport, &otvet4f, sizeof(otvet4f),
&lpNumberOfBytesWritten, NULL );
crc_otv_flt.integ=CRC16(&otvet4f._1from_addr,7);
if ((crc_otv_flt.bytes[0]==otvet4f._8crc1)&& (crc_otv_flt.bytes[1]==otvet4f._9crc2))
{
p=4;
}
else
{
ZeroMemory(&otvet4f,sizeof(otvet4f));
}
}
}
if (floatNotNumber(otvet4f._4data))
return 0;
else
return IEEE754float(otvet4f._4data);
}
//-------------------------------------------------------------------------
//вспомогательная функция - преобразование 4 байт в число float
float __fastcall IEEE754float(BYTE src[4])
{
BYTE aaa;
float ffloat;
if (BoolInvertedBytes)
{
aaa=src[0];
src[0]=src[3];
src[3]=aaa;
aaa=src[1];
src[1]=src[2];
src[2]=aaa;
}
_wmemcpy(&ffloat,src,4);
return ffloat;
}
//--------------------------------------------------------------------------
//вспомогательная функция - проверка согласно IEEE754
BOOL __fastcall floatNotNumber(BYTE src[4])
{
return (((src[0]==0x7F) || (src[0]==0xFF)) && (src[1]>0x7F)
& ((src[1]>0x80) || (src[2]>0x00) || (src[3]>0x00)));
}
//---------------------------------------------------------------------------
//прочитать значение типа long из modbus-slave устройства с адресом mbadr, адрес регистров - addr_hi, addr_lo
long ReadLong(BYTE mbadr,BYTE addr_hi,BYTE addr_lo)
{
crc_union crc_long, crc_otv_lng;
otvet4 otvet4l;
DWORD p;
crc_long.integ=0;
zapros._1addr=mbadr;
zapros._2cmd=0x03;
zapros._3data_start_addr_hi=addr_hi;
zapros._4data_start_addr_lo=addr_lo;
zapros._5data_length_hi=0x00;
zapros._6data_length_lo=0x02;
crc_long.integ=CRC16(&zapros._1addr,6);
zapros._7crc1=crc_long.bytes[0];
zapros._8crc2=crc_long.bytes[1];
for(p=0;p<3;p++)
{
ZeroMemory(&otvet4l,sizeof(otvet4l));
PurgeComm(Comport,PURGE_TXCLEAR);
PurgeComm(Comport,PURGE_RXCLEAR);
if (WriteFile( Comport, &zapros, sizeof(zapros),
&lpNumberOfBytesWritten, NULL ))
{
Sleep(IntEditTimeout);
ClearCommError(Comport,NULL,&statistic);
if (statistic.cbInQue>=sizeof(otvet4l))
ReadFile( Comport,&otvet4l,sizeof(otvet4l),
&lpNumberOfBytesWritten, NULL );
crc_otv_lng.integ=CRC16(&otvet4l._1from_addr,7);
if ((crc_otv_lng.bytes[0]==otvet4l._8crc1) && (crc_otv_lng.bytes[1]==otvet4l._9crc2))
{
p=4;
}
}}
return IEEE754long(otvet4l._4data);
}
//-------------------------------------------------------------------------
//вспомогательная функция - преобразование 4 байт в число long (собственно к стандарту IEEE754 отношения не имеет, просто для не знаю чего)
long __fastcall IEEE754long(BYTE src[4])
{
BYTE aaa;
long ilong;
if (BoolInvertedBytes)
{
aaa=src[0];
src[0]=src[3];
src[3]=aaa;
aaa=src[1];
src[1]=src[2];
src[2]=aaa;
}
_wmemcpy(&ilong,src,4);
return ilong;
}
//-------------------------------------------------------------------------
//вспомогательная функция - преобразование 8 байт в число double
double __fastcall IEEE754double(BYTE src[8])
{
BYTE aaa;
double ddouble;
if (BoolInvertedBytes)
{
aaa=src[0];
src[0]=src[7];
src[7]=aaa;
aaa=src[1];
src[1]=src[6];
src[6]=aaa;
aaa=src[2];
src[2]=src[5];
src[5]=aaa;
aaa=src[3];
src[3]=src[4];
src[4]=aaa;
}
_wmemcpy(&ddouble,src,8);
return ddouble;
}
//-------------------------------------------------------------------------
//прочитать значение типа double из modbus-slave устройства с адресом mbadr, адрес регистров - addr_hi, addr_lo
double ReadDouble(BYTE mbadr,BYTE addr_hi,BYTE addr_lo)
{
crc_union crc_double, crc_otv_dbl;
otvet8 otvet8d;
DWORD p;
crc_double.integ=0;
zapros._1addr=mbadr;
zapros._2cmd=0x03;
zapros._3data_start_addr_hi=addr_hi;
zapros._4data_start_addr_lo=addr_lo;
zapros._5data_length_hi=0x00;
zapros._6data_length_lo=0x04;
crc_double.integ=CRC16(&zapros._1addr,6);
zapros._7crc1=crc_double.bytes[0];
zapros._8crc2=crc_double.bytes[1];
for(p=0;p<3;p++)
{
ZeroMemory(&otvet8d,sizeof(otvet8d));
PurgeComm(Comport,PURGE_TXCLEAR);
PurgeComm(Comport,PURGE_RXCLEAR);
if (WriteFile (Comport, &zapros,sizeof(zapros),
&lpNumberOfBytesWritten, NULL ))
{
Sleep(IntEditTimeout);
ClearCommError(Comport,NULL,&statistic);
if (statistic.cbInQue>=sizeof(otvet8d))
ReadFile( Comport,&otvet8d, sizeof(otvet8d),
&lpNumberOfBytesWritten, NULL );
crc_otv_dbl.integ=CRC16(&otvet8d._1from_addr,11);
if ((crc_otv_dbl.bytes[0]==otvet8d._Ccrc1) && (crc_otv_dbl.bytes[1]==otvet8d._Dcrc2))
{
p=4;
}
}
}
return IEEE754double(otvet8d._4data);
}
//---------------------------------------------------------------------------