Форум программистов, компьютерный форум, киберфорум
АСУ ТП, промэлектроника
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.96/83: Рейтинг темы: голосов - 83, средняя оценка - 4.96
0 / 0 / 0
Регистрация: 04.06.2013
Сообщений: 14
1

Протокол Modbus RTU/ASCII для режима Slave

04.06.2013, 17:25. Показов 16213. Ответов 36

Author24 — интернет-сервис помощи студентам
Здравствуйте!
Стоит задача реализовать протокол Modbus ASCII/RTU для режима Slave на Си. Программа modbus_tester будет опрашивать программу через последовательный порт. Значения регистров должны загружаться из файла при запуске программы и сохраняться в файл при выходе из программы.
Программировать на Си начал совсем недавно, поэтому эта задача для меня совсем не проста и разобраться в тоннах исходных кодов типа freemodbus не удалось.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
04.06.2013, 17:25
Ответы с готовыми решениями:

Нет связи между модулем ввода (modbus rtu slave) и программой modbus poll на ноутбуке
Добрый день форумчанам. Сконфигурирован модуль modbus rtu slave в Ovation system. Опыт работы с...

Эмулятор для Modbus RTU (slave и master) под Linux
Необходим эмулятор для отладки modbus rtu (slave и master). Работа предполагается только через...

Modbus RTU и ASCII на одном интерфейсе
Возникла ситуация следующего рода. На один интерфейс посажены несколько приборов (одних и тех же)....

Запрос MODBUS RTU
Добрый день! Никак не получается получить ответ от МВА8. По протоколу ascii проблем не...

36
...
1910 / 1329 / 966
Регистрация: 12.02.2013
Сообщений: 2,172
04.06.2013, 17:45 2
GhostSolder, так всё же не понятно, какого рода помощь вы рассчитываете получить?

Цитата Сообщение от GhostSolder Посмотреть сообщение
протокол Modbus ASCII/RTU
ASCII(символьный) и RTU(бинарный) это разновидности протокола. Вам нужно оба реализовать или какой-то один?
0
0 / 0 / 0
Регистрация: 04.06.2013
Сообщений: 14
04.06.2013, 17:58  [ТС] 3
Любую помощь по реализации. Реализовать нужно оба протокола.
0
...
1910 / 1329 / 966
Регистрация: 12.02.2013
Сообщений: 2,172
04.06.2013, 19:09 4
Хм... Я даже и не знаю с чего начать . Сам протокол очень прост и не имеет ничего замудрённого. Основные пакеты протокола выглядят следующим образом:
Запрос каких либо данных:
<АДРЕС УСТРОЙСТВА> <ФУНКЦИЯ> <АДРЕС УЧАСТКА ПАМЯТИ> <КОЛИЧЕСТВО ЗАПРАШИВАЕМЫХ ДАННЫХ> <CRC>
Отправка данных:
<АДРЕС УСТРОЙСТВА> <ФУНКЦИЯ> <АДРЕС УЧАСТКА ПАМЯТИ> <КОЛИЧЕСТВО ДАННЫХ НА БОРТУ> <ДАННЫЕ> <CRC>

Для возможности разработки и тестирования вам обязательно нужно вооружиться сниффером последовательных портов (к примеру он есть в sysinternals и называется portmon). Затем нужна утилита которая будет эмулировать master-a, к примеру вот эта. И самое главное, вот этой замечательной штуковиной: Virtual Serial Ports Emulator. Она создаёт виртуальный com-порт и позволяет подключаться к нему сразу нескольким процессам.

В качестве первого шага советую вооружиться документацией по modbus. Создать виртуальный com-порт. Подключиться к нему сниффером. И с помощью какого-либо приложения работающего по modbus протоколу пытаться выполнить какие-либо запросы через созданный порт. А там всё само собой начнёт выстраиваться.
1
0 / 0 / 0
Регистрация: 04.06.2013
Сообщений: 14
04.06.2013, 19:44  [ТС] 5
Спасибо =) Но проблемы возникают именно в реализации этого всего на Си. То есть всё что у меня на данный момент есть это modbus-tester, VSPE и вот это
C
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
void main()
{
       FILE* com;
       com=fopen("com1","rb");
       fclose(com);
}
Ещё совсем не понятно как значения регистров должны загружаться из файла и потом сохраняться.
0
Почетный модератор
Эксперт по компьютерным сетямЭксперт Windows
28046 / 15779 / 983
Регистрация: 15.09.2009
Сообщений: 67,752
Записей в блоге: 78
04.06.2013, 19:51 6
посмотрите закрепленные и не только темы здесь: https://www.cyberforum.ru/asutp/
(если желаете я всю тему перенесу туда, мне кажется там Вы помощь получите быстрее)
1
0 / 0 / 0
Регистрация: 04.06.2013
Сообщений: 14
04.06.2013, 19:54  [ТС] 7
Спасибо, думаю действительно рациональнее будет перенести эту тему туда.
0
...
1910 / 1329 / 966
Регистрация: 12.02.2013
Сообщений: 2,172
04.06.2013, 20:05 8
Цитата Сообщение от GhostSolder Посмотреть сообщение
Ещё совсем не понятно как значения регистров должны загружаться из файла и потом сохраняться.
Так это же просто. По сути дела регистры это адреса в памяти вашего виртуального PLC, т.е. это некий массив, ведь так? Такой массив вы должны сохранять и загружать из файла и естественно в бинарном виде. Предположим, что у вашей виртуальной машины памяти 65536 байт:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
 
#define SIZE 65536
 
int main()
{
   // Память
   char mem[SIZE] = { 0 };
 
   // Загрузка при старте
   FILE* f = fopen("mem.dat", "rb");
   if (f)
   {
      fread(mem, SIZE, 1, f);
      fclose(f);
   }
 
   // ...
 
   // Выгрузка при завершении
   f = fopen("mem.dat", "wb");
 
   if (f)
   {
      fwrite(mem, SIZE, 1, f);
      fclose(f);
   }
 
   return 0;
}
1
10234 / 6612 / 498
Регистрация: 28.12.2010
Сообщений: 21,154
Записей в блоге: 1
04.06.2013, 20:23 9
RS-485. Работа с Modbus протоколом...
Libmodbus. Библиотека Modbus for Linux, Mac OS X, FreeBSD, QNX and Win32
(материала и примеров реализации, в том числе и кроссплатформенного, достаточно)
0
3176 / 1935 / 312
Регистрация: 27.08.2010
Сообщений: 5,131
Записей в блоге: 1
04.06.2013, 21:46 10
Если за десять лет ничего не поменялось, то должно работать :-)
Вложения
Тип файла: rar mbw.rar (60.2 Кб, 317 просмотров)
1
0 / 0 / 0
Регистрация: 04.06.2013
Сообщений: 14
04.06.2013, 22:53  [ТС] 11
raxp, Материала и примеров реализации, не спорю, предостаточно, но, к сожалению, мои знания не позволяют самостоятельно разобраться в этом всём.

gazlan, Спасибо, но проект при компиляции выдаёт ошибку fatal error RC1110: could not open .\mbw.rc

anmartex, А массив получается изначально инициализируется нулевыми значениями или это от чего-то зависит? (извините если задаю глупые вопросы)
0
10234 / 6612 / 498
Регистрация: 28.12.2010
Сообщений: 21,154
Записей в блоге: 1
04.06.2013, 23:04 12
raxp, Материала и примеров реализации, не спорю, предостаточно, но, к сожалению, мои знания не позволяют самостоятельно разобраться в этом всём.
никто кроме вас самих эти знания в вас не впихнет. Что касается сожаления, сожалею что вы столь невнимательны, ибо готовое, готовое с исходниками дается, ленивый только не подберет/
1
3176 / 1935 / 312
Регистрация: 27.08.2010
Сообщений: 5,131
Записей в блоге: 1
04.06.2013, 23:26 13
Цитата Сообщение от GhostSolder Посмотреть сообщение
could not open .\mbw.rc
Должен подойти. В крайнем случае, отредактируйте. В том проекте, он почему-то потерян.
Вложения
Тип файла: 7z mbw.rc.7z (2.4 Кб, 90 просмотров)
1
...
1910 / 1329 / 966
Регистрация: 12.02.2013
Сообщений: 2,172
05.06.2013, 04:31 14
Цитата Сообщение от GhostSolder Посмотреть сообщение
anmartex, А массив получается изначально инициализируется нулевыми значениями или это от чего-то зависит? (извините если задаю глупые вопросы)
Извиняться незачем. Массив конечно изначально инициализируется нулевыми значениями, и в том случае если не найдётся файл mem.dat, то он таким и останется. Но в случае наличия mem.dat (строка 12) произойдёт загрузка данных из файла (строка 14).

У вас должно быть 4-ре таких массива, ибо спецификация протокола определяет четыре таблицы данных:

Дискретные входы (Discrete Inputs)один биттолько чтение1 (0x01)
Регистры флагов (Coils)один битчтение и запись2 (0x02)
Регистры ввода (Input Registers)16-битное словотолько чтение3 (0x03)
Регистры хранения (Holding Registers)16-битное словочтение и запись4 (0x04)

Если мы массив, приведённый мною в предыдущем комментарии, отнесём к регистру хранения, то размерность его должна составлять 2*65536 элементов.

Теперь, как только будет производиться запрос к вашему виртуальному устройству (протокол RTU):
0x01 0x04 0x00 0x10 0x00 0x02 <CRC>

где: серый - адрес устройства; зелёный - регистр; синий - адрес регистра (индекс вашего массива); красный - количество запрошенных элементов

вы должны будите отправить 4 байта, т.е. 4 элемент массива: 32, 33, 34 и 35. Примерно так это будет выглядеть:
0x01 0x04 0x04 0x00 0x00 0x00 0x00 <CRC>

где: серый - адрес устройства; зелёный - регистр; красный - количество возвращаемых данных; розовый - данные
1
0 / 0 / 0
Регистрация: 04.06.2013
Сообщений: 14
06.06.2013, 20:36  [ТС] 15
Цитата Сообщение от anmartex Посмотреть сообщение
0x00 0x02
Цитата Сообщение от anmartex Посмотреть сообщение
красный - количество запрошенных элементов
Цитата Сообщение от anmartex Посмотреть сообщение
вы должны будите отправить 4 байта
anmartex, не очень понял почему отправляем 4 байта, ведь запросили 2?
0
10234 / 6612 / 498
Регистрация: 28.12.2010
Сообщений: 21,154
Записей в блоге: 1
06.06.2013, 22:52 16
...может все-таки почитаете теорию и стандарт, а? Потому как тема превращается в обмусоливание и копипаст оттуда-сюда. Не ленитесь.
0
0 / 0 / 0
Регистрация: 04.06.2013
Сообщений: 14
06.06.2013, 23:04  [ТС] 17
raxp, в результате прочтения теории и возник этот вопрос.
Цитата Сообщение от anmartex Посмотреть сообщение
0x01 0x04 0x00 0x10 0x00 0x02 <CRC>
разве на этот запрос мы должны выслать 4 элемента, а не 2?
0
10234 / 6612 / 498
Регистрация: 28.12.2010
Сообщений: 21,154
Записей в блоге: 1
06.06.2013, 23:37 18
Запросили мы два регистра. В ответе RTU значения регистров хранения и регистров ввода передаются начиная с указанного адреса, по два байта на регистр, старший байт каждого регистра передаётся первым. Итого сколько? Это из документации. Еще один подобный вопрос, тема будет закрыта ввиду вашего нежелания ознакомиться со стандартом.
1
...
1910 / 1329 / 966
Регистрация: 12.02.2013
Сообщений: 2,172
07.06.2013, 03:45 19
Цитата Сообщение от GhostSolder Посмотреть сообщение
anmartex, не очень понял почему отправляем 4 байта, ведь запросили 2?
Цитата Сообщение от GhostSolder Посмотреть сообщение
разве на этот запрос мы должны выслать 4 элемента, а не 2?
Регистры ввода и хранения (0х03 и 0х04 соответственно) являются 16-тибитными т.е. на каждый регистр отводится по 2 байта. Отсюда: 2 байта на регистр * 2 запрошенных регистра = 4 байта
1
0 / 0 / 0
Регистрация: 04.06.2013
Сообщений: 14
07.06.2013, 19:25  [ТС] 20
Посмотрите пожалуйста код, всё ли я правильно делаю?
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
 
#define SIZE 65536
#define SIZE1 8
#define ADDR 0x01
 
unsigned int CRC(unsigned char *lMessage, unsigned int lCount)
{
    unsigned int data;
    unsigned char lBits,lCnt;
    data=0xFFFF;
    for(lCnt=0;lCnt<lCount;lCnt++)
    {
        data^=lMessage[lCnt];
        for(lBits=0;lBits<8;lBits++)
        {
            if(data & 0x0001)
            {
                data>>=1;
                data^=0xA001;
            }
            else data>>=1;
        }
    }
    return(data);
}
 
void main()
{   
    HANDLE Com;
    FILE *DIf,*Cf,*IRf,*HRf;
    unsigned char zapr[SIZE1] = { 0 }, otv[SIZE] = { 0 }, addr, func;
    unsigned short param1,param2,zapcrc, IR[SIZE] = { 0 }, HR[SIZE] = { 0 }, DI[SIZE] = { 0 }, C[SIZE] = { 0 };
    unsigned int prcrc;
    int bytesread, byteswrite, i, sizeotv;
    setlocale(LC_ALL,"russian");
    Com = CreateFile("com2", GENERIC_READ, 0,NULL, OPEN_EXISTING, 0, NULL);
    if (Com == INVALID_HANDLE_VALUE) 
    {
        printf("Ком-порт невозможно открыть\n");
        ExitProcess(1);
    }
    ReadFile(Com,zapr,SIZE1,&bytesread,NULL);
    CloseHandle(Com);
    addr=zapr[0];
    func=zapr[1];
    param1=(zapr[2]<<8)+zapr[3];
    param2=(zapr[4]<<8)+zapr[5];
    zapcrc=(zapr[7]<<8)+zapr[6];
    prcrc=CRC(zapr,SIZE1-2);
    if(zapcrc==prcrc)
    {
        if (ADDR==addr)
        {   
            printf("Получен запрос\n");
            for(i=0;i<SIZE1;i++) 
                printf("[%02x]",zapr[i]);
            printf("\n");
            DIf=fopen("DIf.dat","rb");
            if(DIf)
            {
                fread(DI,SIZE,1,DIf);
                fclose(DIf);
            }
            Cf=fopen("Cf.dat","rb");
            if(Cf)
            {
                fread(C,SIZE,1,Cf);
                fclose(Cf);
            }
            IRf=fopen("IRf.dat","rb");
            if(IRf)
            {
                fread(IR,SIZE*2,1,IRf);
                fclose(IRf);
            }
            HRf=fopen("HRf.dat","rb");
            if(HRf)
            {
                fread(HR,SIZE*2,1,HRf);
                fclose(HRf);
            }
            switch(func)
            {
            case 0x01: //получение текущего состояние группы логических ячеек (С)
                {   
                    unsigned char kolvo, a = 0, kol[250][8]= { 0 }, kol1[250] = { 0 }; int j;
                    if(param2%8==0) kolvo=param2/8;
                    if(param2%8>0) kolvo=param2/8+1;
                    for(i=0;i<kolvo;i++)
                        for(j=8;j>0;j--)
                        {
                            if(C[param1+i]==0xFF00) kol[i][j]=1;
                            param1++;
                        }
                        otv[0]=zapr[0];
                        otv[1]=zapr[1];
                        otv[2]=kolvo;
                        for(i=0;i<kolvo;i++)
                        {
                            for(j=0;j<8;j++)
                                a|=kol[i][j]<<j;
                            kol1[i]=a;
                        }
                        for(i=3;i<kolvo+3;i++) 
                            otv[i]=kol1[i-3];
                        prcrc=CRC(otv,kolvo+3);
                        otv[kolvo+3]=prcrc;
                        otv[kolvo+4]=prcrc>>8;
                        sizeotv=kolvo+5;
                }
                break;
            case 0x02: //получение текущего состояния группы дискретных входов (DI)
                {
                    unsigned char kolvo, a = 0, kol[250][8]= { 0 }, kol1[250] = { 0 }; int j;
                    if(param2%8==0) kolvo=param2/8;
                    if(param2%8>0) kolvo=param2/8+1;
                    for(i=0;i<kolvo;i++)
                        for(j=8;j>0;j--)
                        {
                            if(DI[param1+i]==0xFF00) kol[i][j]=1;
                            param1++;
                        }
                        otv[0]=zapr[0];
                        otv[1]=zapr[1];
                        otv[2]=kolvo;
                        for(i=0;i<kolvo;i++)
                        {
                            for(j=0;j<8;j++)
                                a|=kol[i][j]<<j;
                            kol1[i]=a;
                        }
                        for(i=3;i<kolvo+3;i++) 
                            otv[i]=kol1[i-3];
                        prcrc=CRC(otv,kolvo+3);
                        otv[kolvo+3]=prcrc;
                        otv[kolvo+4]=prcrc>>8;
                        sizeotv=kolvo+5;
                }
                break;
            case 0x03: //получение текущего значения одного или нескольких регистров хранения (HR)
                {
                    unsigned char kolvo, kol1[250] = { 0 }; int j;
                    kolvo=param2*2;
                    for(i=0;i<kolvo;i+=2)
                    {
                        kol1[i]=HR[param1]>>8;
                        kol1[i+1]=HR[param1];
                        param1++;
                    }
                    otv[0]=zapr[0];
                    otv[1]=zapr[1];
                    otv[2]=kolvo;
                    for(i=3;i<kolvo+3;i++) 
                        otv[i]=kol1[i-3];
                    prcrc=CRC(otv,kolvo+3);
                    otv[kolvo+3]=prcrc;
                    otv[kolvo+4]=prcrc>>8;
                    sizeotv=kolvo+5;
                }
                break;
            case 0x04: //получение текущего значения одного или нескольких входных регистров (IR)
                {
                    unsigned char kolvo, kol1[250] = { 0 }; int j;
                    kolvo=param2*2;
                    for(i=0;i<kolvo;i+=2)
                    {
                        kol1[i]=IR[param1]>>8;
                        kol1[i+1]=IR[param1];
                        param1++;
                    }
                    otv[0]=zapr[0];
                    otv[1]=zapr[1];
                    otv[2]=kolvo;
                    for(i=3;i<kolvo+3;i++) 
                        otv[i]=kol1[i-3];
                    prcrc=CRC(otv,kolvo+3);
                    otv[kolvo+3]=prcrc;
                    otv[kolvo+4]=prcrc>>8;
                    sizeotv=kolvo+5;
                }
                break;
            case 0x05: //изменение логической ячейки в состояние ON - 0xFF00, OFF - 0x0000 (C)
                {
                    if((param2==0xFF00) || (param2==0x0000)) 
                    {
                        C[param1]=param2;
                        for(i=0;i<SIZE1;i++) 
                            otv[i]=zapr[i];
                        sizeotv=SIZE1;
                    }
                }
                break;
            case 0x06: //запись нового значения в регистр хранения (HR)
                {
                    HR[param1]=param2;
                    for(i=0;i<SIZE1;i++) 
                        otv[i]=zapr[i];
                    sizeotv=SIZE1;
                }
                break;
            case 0x0F: //изменить состояние нескольких последовательных логических ячеек (C)
                {
 
                }
                break;
            case 0x10: //установить новые значения нескольких последовательных регистров (HR)
                {
 
                }
                break;
            default: printf("Запрошена неописанная функция\n");
            }
            Com = CreateFile("com2", GENERIC_WRITE, 0,NULL, OPEN_EXISTING, 0, NULL);
            if (Com == INVALID_HANDLE_VALUE) 
            {
                printf("Ком-порт невозможно открыть\n");
                ExitProcess(1);
            }
            WriteFile(Com,otv,sizeotv,&byteswrite,NULL);
            CloseHandle(Com);
            printf("Выслан ответ\n");
            for(i=0;i<sizeotv;i++) 
                printf("[%02x]",otv[i]);
            printf("\n");
            DIf=fopen("DIf.dat","wb");
            if(DIf)
            {
                fwrite(DI,SIZE,1,DIf);
                fclose(DIf);
            }
            Cf=fopen("Cf.dat","wb");
            if(Cf)
            {
                fwrite(C,SIZE,1,Cf);
                fclose(Cf);
            }
            IRf=fopen("IRf.dat","wb");
            if(IRf)
            {
                fwrite(IR,SIZE*2,1,IRf);
                fclose(IRf);
            }
            HRf=fopen("HRf.dat","wb");
            if(HRf)
            {
                fwrite(HR,SIZE*2,1,HRf);
                fclose(HRf);
            }
        }
    }
}
0
07.06.2013, 19:25
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
07.06.2013, 19:25
Помогаю со студенческими работами здесь

Библиотека MODBUS ASCII для atmega328
Помогите найти библиотеку MODBUS ASCII для atmega328, которая скомпилируется в Atmel Studio. То,...

Modbus RTU. Oпрос прибора
подскажите пожалуйста, что-то явно делаю не так, прибор опрашиваю по modbus rtu , но в ответ в...

Интеграция с контроллером по ModBus RTU
Здравствуйте. В щите используется контроллер, который опрашивает модули, датчики и т.п. по modbus...

Расшифровка данных Modbus RTU
Добрый день. Подскажите как расшифровать данные от прибора ? Снимаю снифером обмен данными через...

Modbus. Опрос slave
Здравствуйте. Имеется десяток модулей цифрового ввода, которые опрашиваю программой, написанной с...

Modbus-RTU. Синхронизация. Формат времени
Здравствуйте! Помогите разобраться с форматом времени. Прибор- БМРЗ-100. В описании протокола...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru