0 / 0 / 0
Регистрация: 22.11.2022
Сообщений: 4
1

[Segnetics matrix] Отправка команды по RS-485

22.11.2022, 21:19. Показов 221. Ответов 7
Метки нет (Все метки)

Приношу свои извинения. В теме программирования ПЛК совсем не давно. Поставили задачу написать программу для ПЛК (segnetics matrix) для отслеживания массы груза. Используются промышленные весы. Производитель весового оборудования ответил, что для получения веса необходимо отправить команду D0 на адрес 0
Кликните здесь для просмотра всего текста

Все данные в ASCII кодах, конец команды - символы с кодами 0x0D 0x0A или просто 0x0A
 
Запрос веса:
D0, где 0 - адрес, установленный в настройках
 
Ответ:
W008700+0124 C2
где 0 - адрес ответившего терминала,
08 - режим работы
7 - состояние входов, 7 значит все три входа в состоянии лог. 1.
Бит 0 соответствует датчику положения контейнера,
бит 1 - датчик заслонки,
бит 2 - датчик каретки (каплесбора?).
00 - состояние выходов, в шестнадцатеричной системе. 00 - все выключено, 1F - все включено
бит 0 - транспортер
бит 1 - загрузка
бит 2 - насос
бит 3 - каретка
бит 4 - зумер
+0124 - вес.
С2 - контрольная сумма.


Программу для контролера можно писать только на FBD.
Прошу помощи, объясните пожалуйста как отправить команду D0 и обработать ответ. Общение ПЛК с весами происходит по RS-485, протокол неизвестен.

На сколько я понял то команда D0 должна отправляется при помощи ST (не силен в нем) как-то так:

Код
//Код команды 
             data[0] = 44; // D
//Адрес устройства
            data[1] = 33; // 0
И еще вопрос.
Отправить команду и прочитать регистр это разные понятия? Хотя в случае чтения регистра, мы точно знаем функцию чтения/записи, адрес устройства, номер регистра, в моем же случае просто отправить команду.
__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
22.11.2022, 21:19
Ответы с готовыми решениями:

Отправка и получение данных через RS-485
Помогите кто чем сможет. Надо отправить 2 командных пакетов из 7-ми байтов. Пауза между командными...

Проверка подключения RS-485 через преобразователь USB/RS-485
Добрый вечер, форумчане! Я относительно навичек софт-программист, и уж тем более навичек в...

Отправка команды
добречка всем имеется класс public class WC3Send { public WC3Send(); ...

Какова суть процедур create matrix, output matrix, change row?
Что за процедуры create matrix, output matrix, change row?

7
Модератор
Эксперт по электронике
8190 / 4117 / 1568
Регистрация: 01.02.2015
Сообщений: 12,724
Записей в блоге: 3
22.11.2022, 22:20 2
ПЛК построен на базе ARM-процессора с частотой 1ГГц, под управлением ОС Linux и программируется с помощью инструментальных средств Segnetics: SMLogix, SMArt, SMConstructor и других. Операционная система позволяет запускать собственные программы, написанные на общедоступных языках (С/С++, java, python, php, js и прочие) и пользоваться всеми встроенными средствами доступа и управления периферией.
Затрудняюсь в выборе средств программирования...
Нужно читать руководство по программированию, чтобы понять доступные средства.

Из Вашего описания протокола обмена с весами понял, что нужно
1. в весах выставить сетевой адрес - например, 2 (часто бывает, что 0 и 1 предопределены и закреплены для особых запросов или устройств)
2. в порт подать последовательность из двух байт D2 0A
3. ожидать приёма строки вида W008700+0124 C2
4. анализировать строку на предмет исправности аппаратуры, корректности обмена
5. при исправности - выделить символы 0124 и преобразовать их в число - это и будет значение веса.

Я бы подключил ноутбук и через терминал (putty или другой) отправлял команду и смотрел приём.
Как вариант - сделал бы программку на Pascal с применением пакета Synaser (Вы можете делать на любом другом знакомом языке) для работы с COM портом и смотрел бы за принятыми данными.

Добавлено через 13 минут
В описании Segnetic Matrix не нашёл способа обмена с устройствами, поддерживающими нестандартный протокол.
Возможно, имеет смысл обратиться напрямую в техподдержку.
1
0 / 0 / 0
Регистрация: 22.11.2022
Сообщений: 4
22.11.2022, 23:12  [ТС] 3
Спасибо за ответ. Да все верно, про контроллер. Из коробки доступен только FBD. Из всего анализа нужен только вес, при этом не важно что там с оборудованием (исправно или нет), тут контроллер весов сам аварии обрабатывает. Задача только получить вес.
А в случае FBD это надо байтами оперировать и их уже через rs-485 отправлять, я правильно понял? Ну и разбирать полученный ответ тоже побайтно надо.
0
Модератор
Эксперт по электронике
8190 / 4117 / 1568
Регистрация: 01.02.2015
Сообщений: 12,724
Записей в блоге: 3
22.11.2022, 23:22 4
Да. Вопрос только в возможности обмена по нестандартным протоколам средствами языка. Это лучше прояснит техподдержка.
0
0 / 0 / 0
Регистрация: 22.11.2022
Сообщений: 4
24.11.2022, 15:30  [ТС] 5
Код работы с RS-485

Кликните здесь для просмотра всего текста

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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#include "shm.hpp"
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <rlserial.h>
 
// -----------------------------------------------------------------------------
// Это пример программы обмена со slave устройством.
// Пример рабочий и показывает основные аспекты работы с последовательным портом.
// По реализации, rs485 и rs232 в данном примере не отличаются. Протокол обмена
// взят следующий, в нем есть только один тип запросов и один тип ответа на
// запрос, причем формат запроса и ответа ничем не отличается. Формат запросов:
// - пакет должен начинаться с символа ':' и заканчиваться контрольной суммой
// LRC и последовательностью '\r''\n' (0x0A0x0D), в точности как в modbus ascii.
// Данные по протоколу передаются в ASCII формате, старшим байтом вперед
// также как в modbus ascii, но передается только одно значение слейву и
// одно значение же со слейва считывается. Протокол безадресный, т.е. подключить
// несколько устройств по шине нельзя. Проверка адреса и добавление различных
// функций по спецификациям протоколов задача тривиальная, поэтому в протоколе
// этого примера не рассмотрено, для простоты примера. Некоторые функции написаны
// не самым оптимальным образом, это сделано исключительно для наглядности.
// -----------------------------------------------------------------------------
 
// Наименование интерфейса
// Доступные названия /dev/rs485 /dev/rs232 соотвественно для 485 и 232 интерфейса
// /dev/usbdev /dev/usbhost для виртуальных com портов на usb портах
#define SERIAL_DEVICE       "/dev/rs485"
 
// Таймаут ожидания ответа от slave устройства
#define RESPONSE_TIMEOUT    100000
 
// -----------------------------------------------------------------------------
// TODO: сенкция инициализации глобальных переменных
// -----------------------------------------------------------------------------
 
// Класс работы с портом, из библиотеки rllib, взят для удобства использования 
// и наглядности примера
rlSerial       serial;
 
// Буфер обмена для всех передаваемых данных
unsigned char Msg[256+1];
// Указатель на этот же массив, но с первого элемента, чтобы опустить разделитель
// начала пакета ':'
unsigned char* Data = Msg+1;
 
// -----------------------------------------------------------------------------
// TODO: сенкция обьявления необходимых дополнительных функций
// -----------------------------------------------------------------------------
 
// Функция преобразования ASCII => int, старший байт вперед, как в modbus ascii
int buf2short_ascii(unsigned char *buf){
    int val;
    sscanf((char *) buf,"%4x",&val);
    return val;
}
// Функция преобразования int => ASCII, старший байт вперед, как в modbus ascii
void short2buf_ascii(int i, unsigned char *buf){
    char tmp = buf[4];  // sprintf поставит ноль в конце строки, нам это не надо
    sprintf((char *) buf,"%04x",i);
    buf[4] = tmp;       // вернем на место то, что затерлось нулем
}
 
// Функция подсчета LRC, полный аналог LRC modbus ascii, взять из rllib
// opensource библиотеки.
// Аргументы функции - указатель на место в пакете, где содержатся данные
// а также указатель на буфер, куда скопировать LRC, и длинну данных в пакете
void CalcLRC(char* from, char *to, int len)
{
  unsigned char lrc;
  int i,high,low,val;
 
  if(len < 0) return;
  lrc = 0;
  for(i=0; i<len; i+=2)
  {
    sscanf((const char *) &from[i],   "%1X", &high);
    sscanf((const char *) &from[i+1], "%1X", &low);
    val = high*16 + low;
    lrc += val;
  }
  lrc = ((unsigned char)(-((char) lrc)));
  sprintf((char *) to,"%02X",(unsigned int) lrc);
}
 
// Функция установки LRC для текущего пакета
void SetLRC(int len){
    // Установка LRC в конец пакета
    CalcLRC((char*)Data, (char*)Data+len, len);
}
 
// Функция проверки LRC для текущего пакета
int CheckLRC(int len){
    char result[3];
    // Подсчет LRC
    CalcLRC((char*)Data, result, len);
    // Проверка LRC
    if(result[0] == Msg[len+1] && result[1] == Msg[len+2])
        return 1;
    else
        return -1;
}
 
// Функция чтения пакета из порта, функция записывает данные в глобальный массив
// Msg. Возвращаемое значение: (-1) истек таймаут ожидания данных
//                             (число > 0) количество принятых байт в пакете
int ReadLineASCII(){
    int ret;
    int len = 0;
 
    // Побайтный цикл считывания данных из порта
 
    while(1) {
 
        // Функция select останавливает выполнение программы до тех пор,
        // пока не будут доступны новые данные (функция вернет ненулевое значение)
        // либо не истечет таймаут ожидания данных (функция вернет нулевое значение)
        if(serial.select(RESPONSE_TIMEOUT))
            ret = serial.readChar();    // пришли новые данные в порт
        else return -1; // Истек таймаут ожидания данных
 
        // Если функция ReadChar вернула отрицательное значение
        // выведем код ошибки для диагностики
        if(ret < 0)
            printf("Error in readChar %d", ret);
 
        // Если приняли признак начала пакета, начинаем запись пакета с начала
        if(ret == ':')
            len = 0;
        // Запишем принятый байт в буфер обмена
        Msg[len] = ret;
        // Увеличим индекс текущего элемента в буфере обмена
        // Остаток от деления использован только для защиты от переполнения
        // буфера
        ++len %= sizeof(Msg)-1;
 
        // Определение конца пакета, конец пакета обозначен для наглядности
        // как в modbus ascii - символами \r\n, соответствует коду 0x0A 0x0D
        // т.е. можно было записать if(ret == 0x0D) if(Msg[len-2] == 0x0A)
        // Важно! В других ASCII протоколах могут быть другие огранители
        // начала и конца посылки. Данные же взяты только для наглядности.
        if(ret == '\n'){
            if(Msg[len-2] == '\r')
                return len;
        }
    }
}
 
// Функция отправки пакета, возвращаемое значение - число отправленных байт
// в случае успеха, отрицателное число в случае ошибки
int SendMessage(int datalen){
        // Запишем первый байт - признак начала пакета
    Msg[0] = ':';
    // Установка контрольной суммы
    //в моем случае контрольная сумма не нужна
    //SetLRC(datalen);
    // Установка признаков конца пакета
    Msg[1+datalen] = '\r'; // индекс = 1(старт байт)+(datalen-данные)                  //+2(LRC)
    Msg[1+datalen+1] = '\n'; // индекс = 1(старт байт)+(datalen-данные)+1('\r')     // +(2-LRC)
 
    // Функция передачи пакета в порт, принимает указатель на передаваемый массив,
    // содержащего пакет и размер передаваемого пакета
    //int ret = serial.writeBlock(Msg,1+datalen+2+1+1); // размер = 1(старт байт)+
    int ret = serial.writeBlock(Msg,1+datalen+1+1); 
    // datalen(данные)+2(LRC)+1('\r')+1('\n')
 
    if(ret < 0) // Если возникла ошибка отправки пакета, выведем ее для диагностики
        printf("Error sending %s with %d\n", Msg, ret);
    else // Если пакет отправлен удачно,
        printf("send - %s", Msg);   // выведем его содержимое для диагностики
    return ret;
}
 
// Функция чтения и проверки пакета, возвращаемое значение - число полученных байт
// в случае успеха, отрицателное число в случае ошибки
int ReadMessage(){
    // Вызов функции чтения пакета из порта
    int ret = ReadLineASCII();
 
    // Проверка возвращаемого значения
    if(ret < 0){
        // Значит вышли из функции ReadLineASCII по таймауту
        // Иначе говоря, за отведенное время новых данных не пришло
        printf("read timed out\n");
        return ret;
    }
    // Проверка контрольной суммы
    if(CheckLRC(ret-1-2-2) < 0){
        // Контрольная сумма не совпала, выведем диагнотстическое сообщение
        printf("LRC error\n");
        // И вернем отрицательное значение - чтобы сигнализировать об ошибке
        return -2;
    }
 
    // В конце пакета поставим ноль, чтобы размер пакета правильно определялся
    // библиотечными функциями printf strlen и т.п.
    Msg[ret] = 0;
    printf("received - %s", Msg);
    return ret;
}
 
// -----------------------------------------------------------------------------
// Функция main, с нее начинается выполнение программы
// -----------------------------------------------------------------------------
int main()
{
    // -------------------------------------------------------------------------
    // Секция инициализации локальных переменных функции main
    // -------------------------------------------------------------------------
 
    Shm mymem("./load_files.srv"); //Инициализация переменных разделяемой памяти
    
    // Проверим наличие sharedmem переменных в проекте и корректность типа данных
    //if(mymem.getType("TestValue") != SHORT)     exit(1);
    //if(mymem.getType("master") != SHORT)        exit(1);
    //if(mymem.getType("slave") != SHORT)         exit(1);
    if(mymem.getType("cmd") != FLOAT)    exit(1);
    if(mymem.getType("mass") != FLOAT)     exit(1);  
    
    // TODO: Здесь можно разместить обьявить и
    // инициализировать необходимые вам переменные
 
    // Выведем для диагностики, что программа начала выполнение
    printf("start\n");
 
    // Вызов функции для начала работы с портом и установки параметров связи
    // Здесь можно менять значения параметров кроме 3го и 4го
    // Первый параметр - имя интерфейса, определен макросом SERIAL_DEVICE
    // Второй параметр - скорость обмена по порту (должен быть как у slave)
    // Третий параметр - блокирующие вызовы отправки и приема байт (не менять)
    // Четвертый параметр - использование аппаратного управления (не менять)
    // Пятый параметр - количество байт данных (чаще всего менять не нужно)
    // Шестой параметр - количество стоповых бит (должен быть как у slave)
    // Седьмой параметр - четность (должен быть как у slave)
    if(serial.openDevice(SERIAL_DEVICE,B4800,1,0,8,1,rlSerial::EVEN) < 0)
    {
        printf("Error opening serial\n");
        exit(1);
    }
 
    // Чтение переменной из FBD проекта
    short value = mymem.getShort("cmd");
    // Сохранение этого значения для отслеживания его изменения
    short oldvalue = value;
    // Конвертация значения в ASCII формат, как в Modbus ASCII
    short2buf_ascii(value, Data);
 
    // Структура для управления задержкой между отдельными посылками - 1 секунда
    // по смыслу фактически соответствует периоду обмена
    struct timespec delay;
    delay.tv_sec = 1;
    delay.tv_nsec = 0;
 
    while(1){
 
        // Проверить, если установили из FBD новое значение переменной
        //if(mymem.getBool("cmd") != oldvalue){
        //    oldvalue = mymem.getBool("cmd");
            // Записать новое значение в переменную value
        //    value = oldvalue;
        value = mymem.getShort("cmd");
        }
 
        // Конвертация value в ASCII формат и загрузка value в буфер обмена
        // по указателю Data, который и определяет место переменной в буфере обмена
        short2buf_ascii(value, Data);
 
        // Отправить slave устройству short переменную value, которая
        // к этому моменту уже находится в буфере обмена
        // При преобразовании в ASCII формат все переменные конвертируются в
        // 2 раза большее число байт. Т.е. число типа short - 2 байтное, при
        // преобразовании в ASCII занимает 4 байта. Поэтому длинна передаваемых
        // данных 4 байта, это число и указывается при вызове SendMessage.
        if(SendMessage(4) < 0)
            continue;
 
        // Прочитать в ответ от slave устройства
        if(ReadMessage() < 0)
            continue;
 
        // Сконвертировать значение, содержащееся в приемном буфере в short
        // и сохранить в переменной value
        value = buf2short_ascii(Data);
        // Передать принятое значение опрашиваемой переменной в FBD
        mymem.setShort("mass", value);
 
        // Задержка между отправляемыми пакетами, иначе говоря период обмена
        nanosleep(&delay, NULL);
    }
    // Закрытие порта, фактически в данном примере сюда никогда не попадем
    serial.closeDevice();
 
    // Завершение работы программы
    printf("Close application");
    exit(0);
}


Запрос и получение данных:

Кликните здесь для просмотра всего текста

Запрос веса:
D0, где 0 - адрес, установленный в настройках
 
Ответ:
W008700+0124 C2
где 0 - адрес ответившего терминала,
08 - режим работы
7 - состояние входов, 7 значит все три входа в состоянии лог. 1.
Бит 0 соответствует датчику положения контейнера,
бит 1 - датчик заслонки,
бит 2 - датчик каретки (каплесбора?).
00 - состояние выходов, в шестнадцатеричной системе. 00 - все выключено, 1F - все включено
бит 0 - транспортер
бит 1 - загрузка
бит 2 - насос
бит 3 - каретка
бит 4 - зумер
+0124 - вес.
С2 - контрольная сумма.

Подскажите как из набора получаемых данных
Ответ: W008700+0124 C2 выдернуть только +0124 ?
0
Модератор
Эксперт по электронике
8190 / 4117 / 1568
Регистрация: 01.02.2015
Сообщений: 12,724
Записей в блоге: 3
24.11.2022, 19:41 6
можно так
C
1
2
3
4
int w=0;
for(int i=8; i<12; i++)
  w = w*10 + (s[i]-'0');
printf("вес: %d", w);
0
0 / 0 / 0
Регистрация: 22.11.2022
Сообщений: 4
24.11.2022, 22:47  [ТС] 7
Я понимаю, что перехожу уже все границы, равносильно тому, что прошу за меня кусок кода в нужное место воткнуть, да и еще и правильно его написать, но позвольте еще пару вопросов.

Если я правильно мыслю, приведенный вами код нужно вставить в 284 строку, перед
Код
mymem.setShort("mass", value);
так:

C
1
2
3
4
5
int w=0;
for(int i=8; i<12; i++)
  w = w*10 + (value[i]-'0');
//передаем в FBD
mymem.setShort("mass", w);
Или я в кучу намешал и мух и котлеты и value уже не строка?
Рассуждал так:
Кликните здесь для просмотра всего текста

т.к. строка представляет собой одномерный массив символов оканчивающихся цифрой 0, следовательно, ее можно перебирать по индексам, что собственно вы за меня и сделали. Перебираем все элементы с 8 включительно по 12 не включая (индексация с нуля) и снова кладем их в массив, при этом убираем цифру 0 в конце.
w*10 вот этого я не понял, зачем мы это добавили вперед? (это второй вопрос)


Добавлено через 17 минут

Можно ли прописать отправку команды "D0 \r\n" прямо в коде вот таким образом (точнее будет ли оно правильно работать)

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
int SendMessage(int datalen){
        // Запишем первый байт - признак начала пакета
    //Msg[0] = ':'; //судя по описанию протокола символ начала не нужен
    Msg[0] = '\0'; //не уверен правильно ли так делать
    // Установка контрольной суммы
    //в моем случае контрольная сумма не нужна
    //SetLRC(datalen);
    // Установка признаков конца пакета
    Msg[1] = 'D';
    Msg[2] = '0';
    Msg[3] = ' ';
    Msg[4] = '\r'; 
    Msg[5] = '\n';
 
    // Функция передачи пакета в порт, принимает указатель на передаваемый массив,
    // содержащего пакет и размер передаваемого пакета
    //int ret = serial.writeBlock(Msg,1+datalen+2+1+1); // размер = 1(старт байт)+
    int ret = serial.writeBlock(Msg,6); 
    // datalen(данные)+2(LRC)+1('\r')+1('\n')
 
    if(ret < 0) // Если возникла ошибка отправки пакета, выведем ее для диагностики
        printf("Error sending %s with %d\n", Msg, ret);
    else // Если пакет отправлен удачно,
        printf("send - %s", Msg);   // выведем его содержимое для диагностики
    return ret;
}
0
Модератор
Эксперт по электронике
8190 / 4117 / 1568
Регистрация: 01.02.2015
Сообщений: 12,724
Записей в блоге: 3
25.11.2022, 07:27 8
Сколько помню, символ '\n' зависит от настроек операционки и может быть сразу двумя символами.
Можно же просто присвоить число Msg[3]=0x0A

Добавлено через 13 минут
w*10 - у нас имеется массив цифр, чтобы получить из него число нужно самую левую умножить на 1000, следующую на 100, предпоследнюю на 10 и прибавить последнюю. Т.е. вычисление значения многочлена с одной переменной - которое легко реализуется по схеме Горнера
0124
0*1000+1*100+2*10+4=(((0*10)+1)*10)+2)*10+4

Я не вчитывался в код и не соотносил с именами переменных.
Преобразование вставить сразу после получения пакета - 280 строка.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
25.11.2022, 07:27
Помогаю со студенческими работами здесь

Отправка команды по bluetooth
Здравствуйте! В андроид студио хочу написать небольшую програмку, которая бы отправляла команду...

Отправка команды по UDP
Имеется программа, которая должна принимать команду инициализации и посылать структуру данных в...

Контроллеры компании Segnetics
Всем привет! В этой теме вы можете задавать все интересующие вас вопросы по продукции Segnetics...

Отправка команды в открытый Minecraft
Здравствуйте. Подскажите пожалуйста как отправить сообщение/команду из форты C# в майнкрафт. И по...

Отправка текстовой команды в активный процесс
Здравствуйте, есть запущенный процесс в виде командной строки о состоянии сервера (процесс всегда...

Отправка команды из Windows в Linux (samba)
Здравствуйте. На работе в локальной сети &gt;20 пк с разными версиями винды (ХР,7,10), пару дней...


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

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2022, CyberForum.ru