
ЛР2 Саляхов
.docxФГБОУ ВО
Уфимский университет науки и технологий
Кафедра ВТиЗИ
Отчёт
Лабораторной работе №2
по дисциплине "Сети и телекоммуникации"
«Разработка коммуникационного ПО для физического интерфейса RS-485 и изучение промышленного протокола MODBUS»
Выполнили:
ст. гр. ИВТ-429Б
Саляхов А.Ф.
Хамидуллин А.Р.
Пономарева А.А.
Проверил:
Атарская Е.А.
Уфа 2024
Цель работы: Целью работы является практическое изучение интерфейса физического уровня RS-485 и протокола обмена данными Modbus, а также получение навыков разработки и использования ПО для обмена данными с периферийным устройством.
Ход работы:
Создать виртуальные порты COM5 и COM6:
Виртуальные последовательные порты можно создать с помощью программы Virtual Serial Ports Emulator (VSPE):
Протестировать приложение обмена текстовыми данными server и client
Модернизировать клиент и сервер так, чтобы можно было осуществлять отсылку 3 целочисленных значений и 3 вещественных чисел.
Разработать программу циклического опроса одного регистра источника с адресом 25 в формате RTU.
Разработать программу циклического опроса 16 регистров источника в формате RTU.
Текст программ:
Сервер:
#include <stdio.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <modbus/modbus.h>
//==============================================================================
// float == int32 == char[4]
union ParsedFloat
{
float value;
__int32 integer;
unsigned char byteArray[4];
struct ieee_t
{
unsigned int mantissa : 23;
unsigned int exponenta : 8;
unsigned int sign : 1;
} ieee;
};
//==============================================================================
// Печать представления числа с плавающей точкой в виде набора байт
// в шестандцатиричном формате
void printParsedFloatToHex(ParsedFloat &parsedFloat)
{
printf("Float number: dec = %f, hex = ", parsedFloat.value);
for (int i = 3; i > -1; i--)
printf("0x%02X ", parsedFloat.byteArray[i]);
printf("\n");
}
//==============================================================================
// ПРИМЕР
// Пример прямого и обратного преобразования вещественного числа в двоичное
// представление
void convertFloatNumbers(int numEx)
{
printf("Example %d: Parse float number s*m*2^e\n", numEx);
ParsedFloat ft;
ft.value = 123.123;
// Прямое
printf("Float number = %f\n", ft.value);
printf("s_16 = %x\ts_10 = %u\n", ft.ieee.sign, ft.ieee.sign);
printf("e_16 = %x\te_10 = %u\n", ft.ieee.exponenta, ft.ieee.exponenta);
printf("m_16 = %x\tm_10 = %u\n", ft.ieee.mantissa, ft.ieee.mantissa);
// Печать числа в виде октетов в 16 ричном представлении
printParsedFloatToHex(ft);
}
int main(void)
{
//Prepare a Modbus mapping with 30 holding registers
//(plus no output coil, one input coil and two input registers)
//This will also automatically set the value of each register to 0
modbus_mapping_t *mapping = modbus_mapping_new(0, 1, 30, 2);
if (!mapping) {
fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno));
exit(1);
}
mapping->tab_registers[10] = 123;
mapping->tab_registers[11] = 456;
mapping->tab_registers[12] = 789;
ParsedFloat ft1, ft2, ft3;
ft1.value = 123.123;
ft2.value = 456.456;
ft3.value = 789.789;
for(int j=0; j<4; j++)
{
mapping->tab_registers[13+j] = ft1.byteArray[0+j];
}
for(int j=0; j<4; j++)
{
mapping->tab_registers[17+j] = ft2.byteArray[0+j];
}
for(int j=0; j<4; j++)
{
mapping->tab_registers[21+j] = ft3.byteArray[0+j];
}
modbus_t *ctx = modbus_new_rtu("COM5", 9600, 'N', 8, 1);
if (!ctx) {
fprintf(stderr, "Failed to create the context: %s\n", modbus_strerror(errno));
exit(1);
}
//Set the Modbus address of this slave (to 3)
modbus_set_slave(ctx, 3);
if (modbus_connect(ctx) == -1) {
fprintf(stderr, "Unable to connect: %s\n", modbus_strerror(errno));
modbus_free(ctx);
exit(1);
}
uint8_t req[MODBUS_RTU_MAX_ADU_LENGTH];// request buffer
int len;// length of the request/response
int n = 1;
while(1) {
len = modbus_receive(ctx, req);
if (len == -1) break;
len = modbus_reply(ctx, req, len, mapping);
if (len == -1) break;
mapping->tab_registers[25] = n;
n++;
}
printf("Exit the loop: %s\n", modbus_strerror(errno));
modbus_mapping_free(mapping);
modbus_close(ctx);
modbus_free(ctx);
return 0;
}
Клиент:
#include <stdio.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <stdlib.h>
#include <errno.h>
#include <modbus/modbus.h>
#include <iostream>
union ParsedFloat
{
float value;
__int32 integer;
unsigned char byteArray[4];
struct ieee_t
{
unsigned int mantissa : 23;
unsigned int exponenta : 8;
unsigned int sign : 1;
} ieee;
};
void printParsedFloatToHex(ParsedFloat &parsedFloat)
{
printf("Float number: dec = %f, hex = ", parsedFloat.value);
for (int i = 3; i > -1; i--)
printf("0x%02X ", parsedFloat.byteArray[i]);
printf("\n");
}
#if defined(_WIN32)
#define close closesocket
#endif
int main(void)
{
//Create a new RTU context with proper serial parameters (in this example,
//device name /dev/ttyS0, baud rate 9600, no parity bit, 8 data bits, 1 stop bit)
modbus_t *ctx = modbus_new_rtu("COM6", 9600, 'N', 8, 1);
if (!ctx) {
fprintf(stderr, "Failed to create the context: %s\n", modbus_strerror(errno));
exit(1);
}
if (modbus_connect(ctx) == -1) {
fprintf(stderr, "Unable to connect: %s\n", modbus_strerror(errno));
modbus_free(ctx);
exit(1);
}
//Set the Modbus address of the remote slave (to 3)
modbus_set_slave(ctx, 3);
uint16_t reg[5];// will store read registers values
//Read 5 holding registers starting from address 10
int num = modbus_read_registers(ctx, 10, 3, reg);
if (num != 3) {// number of read registers is not the one expected
fprintf(stderr, "Failed to read: %s\n", modbus_strerror(errno));
}
for (int j=0; j<3; j++)
{
printf("\n%d\n", reg[j]);
}
ParsedFloat ft1, ft2, ft3;
int num1 = modbus_read_registers(ctx, 13, 4, reg);
if (num1 != 4) {// number of read registers is not the one expected
fprintf(stderr, "Failed to read: %s\n", modbus_strerror(errno));
}
for (int j=0; j<4; j++)
{
ft1.byteArray[j] = reg[j];
}
printf("\n%f", ft1.value);
int num2 = modbus_read_registers(ctx, 17, 4, reg);
if (num2 != 4) {// number of read registers is not the one expected
fprintf(stderr, "Failed to read: %s\n", modbus_strerror(errno));
}
for (int j=0; j<4; j++)
{
ft2.byteArray[j] = reg[j];
}
printf("\n\n%f", ft2.value);
int num3 = modbus_read_registers(ctx, 21, 4, reg);
if (num3 != 4) {// number of read registers is not the one expected
fprintf(stderr, "Failed to read: %s\n", modbus_strerror(errno));
}
for (int j=0; j<4; j++)
{
ft3.byteArray[j] = reg[j];
}
printf("\n\n%f\n", ft3.value);
while(true)
{
modbus_read_registers(ctx, 25, 1, reg);
std::cout << "\n" << "Register 25: " << reg[0] << "\n\n";
uint16_t tab_reg[16] = {0};
int ret = modbus_read_registers(ctx, 0, 16, tab_reg);
if (ret == -1) {
std::cerr << "Read error: " << modbus_strerror(errno) << "\n";
break;
}
for (int i = 0; i < ret; i++) {
std::cout << "Register " << i << ": " << tab_reg[i] << "\n";
}
sleep(1);
}
modbus_close(ctx);
modbus_free(ctx);
return 0;
}
Вывод: Мы изучили интерфейс физического уровня RS-485 и протокол обмена данными Modbus, а также получили навыки разработки и использования ПО для обмена данными с периферийным устройством.