Прием и отправка SMS-сообщений с панелей оператора ELHART (пример работы с асинхронным интерфейсом)

Введение

В настоящее время в промышленности применяется широкий спектр способов связи технологических объектов. в качестве примера можно привести одни из наиболее распространённых — беспроводная связь по стандартам GPRS, 3G, 4G и проводные способы по интерфейсам RS-485 или Ethernet.

Они позволяют передавать относительно большие объёмы данных за малые промежутки времени. Однако, не всегда возможно применение указанных способов по разным причинам. К числу таких ограничений можно отнести технические (невозможность прокладки кабеля на местности или низкий уровень сигнала по стандарту GPRS/3G) или экономические (цена автоматических GPRS/3G роутеров выше чем цена GSM-модемов).

Cвязь, использующая пакетную передачу данных, зачастую требует наличия статического IP-адреса опрашиваемого узла сети, а также постоянных расходов на трафик. Поэтому использование сервиса коротких сообщений (SMS) для информирования персонала о ходе технологического процесса, а также управления им, ещё широко распространено. Однако, GSM-модемы не имеют встроенной логики и управляются с помощью АТ-команд.

Панели оператора ELHART имеют возможность программирования на языке С (написание макросов), что позволит в рамках конкретного примера исключить применение ПЛК для управления GSM-модемом. При помощи макросов панель оператора может получать прямой доступ к любому из интерфейсов и записывать в него последовательность символов в определённой кодировке. Расширением данного применения может быть использование макросов для установки соединения панелей оператора ELHART с устройствами, использующими нестандартные протоколы.

1 Настройка ПЛК и GSM-модема

Настройка оборудования заключается в настройке GSM-модема Cinterion BGS2T-RS485+MC1,5/6-ST-3,81 и панели оператора ELHART ЕСР-07. Для реализации данного примера возможно использование любого GSM-модема.

1.1 Настройка GSM-модема

Настройка GSM-модема заключается в настройке скорости обмена, количества битов данных, длины интервала стоп-бита и контроля чётности по используемому интерфейсу. Модем должен быть подключен к персональному компьютеру (ПК) по интерфейсу RS-485 через преобразователь интерфейсов RS-485/USB (например, Delta IFD6500), одновременно он может быть подключен и к панели оператора по тому же интерфейсу. Интерфейс RS-485 выбран для удобства, возможно использование RS-232 и, соответственно, модема с таким интерфейсом.

Схема подключения модема к панели оператора и ПК
Рисунок 1 — Схема подключения модема к панели оператора и ПК

Для настройки модема возможно использовать любую программу для ПК, позволяющую записывать данные непосредственно в СОМ-порт (в данном примере используется ComPUMP).

Подача АТ-команды в интерфейс модема
Рисунок 2 — Подача АТ-команды в интерфейс модема

Настройка скорости обмена GSM-модулей модемов, как правило, заключается в подаче АТ-команды «AT+IPR=9600». После последнего символа (в данном случае это «0») необходимо добавить символы возврата каретки и перевода строки, их коды в таблице ASCII - 0D и 0A соответственно. Программа ComPUMP должна изначально иметь настройки скорости обмена, количества битов данных, длины интервала стоп-бита и контроля чётности, соответствующие настройкам модема. в случае успешного приёма и исполнения команды модем ответит «AT+IPR=OK». Скорость обмена 9600 бод выбрана для примера и будет использоваться далее при настройке интерфейса RS-485 панели оператора.

Настройку скорости обмена также можно произвести с панели оператора, добавив соответствующий код в макрос, в данном примере это показано ниже. Настройки проверки чётности, количества стоп-битов и битов данных остаются по умолчанию.

Настройка модема завершена.

1.2 Настройка сенсорной панели оператора ELHART

Настройка сенсорной панели оператора производится на примере модели ECP-07 и в данном случае сводится к написанию макроса. Графический интерфейс используется для визуализации процесса. Основными задачами при отправке SMS являются:

  • открытие указанного интерфейса,
  • запись символов в интерфейс из буфера отправки,
  • ожидание ответа (пауза),
  • получение символов из интерфейса в буфер приёма;

Основными задачами при приёме:

  • циклический опрос содержимого интерфейса для определения признака получения SMS,
  • чтение SMS,
  • выполнение полученной команды.

для создания макроса необходимо перейти в пункт меню «Макросы» - «Главный макрос» - «Создать макрос»:

Создание макроса
Рисунок 3 — Создание макроса

Для макросов панелей оператора ELHART существуют понятия внешних и внутренних переменных. Внешними переменными являются все переменные из области памяти панели (например, область памяти LW). Внутренние переменные объявляются в теле макроса.

1.2.1 Объявление внешних переменных макроса

Объявление необходимых внешних переменных
Рисунок 4 — Объявление необходимых внешних переменных

Внешними переменными в данном примере являются:

1. error_com_param: возвращает значение «0» в случае ошибки установки параметров интерфейса, «1» - в случае удачной передачи; адрес

2. error_outport: возвращает значение «-1» в случае ошибки передачи, в случае удачной передачи возвращает число переданных байтов;

3. error_inport: возвращает значение «-1» в случае ошибки приёма, в случае удачного приёма возвращает число принятых байтов;

4. Imm_alarm: стартовый адрес внешних битов регистров панели, запускающих цикл отправки SMS;

5. modemok: стартовый адрес байтовых регистров для записи строки признака успешной настройки модема;

6. LB50: битовый адрес, изменяемый по команде в SMS-сообщении;

7. Pressure: стартовый адрес байтовых регистров для записи значений в отправляемое SMS-сообщение;

8. number_input: байтовый регистр (тип данных «string») для ввода номера телефона.

1.2.2 Элементы визуализации экрана операторской панели

Соответственно указанным на Рисунке 4 адресам регистров необходимо создать элементы визуализации на экране панели:

Пример визуализации экрана панели оператора
Рисунок 5 — Пример визуализации экрана панели оператора

Элементу ввода «Ввод телефонного номера без +7» соответствует адрес LW3 (тип данных «строка», количество символов — 10, кодировки ASCII и Unicode), элементу «Имитация события No1» - LB0, «Значение параметра No1» - LW16 (тип данных «целое число», 1 разряд до запятой, 2 после), «Значение параметра No2» - LW17 (тип данных «целое число»), «Имитация события No4» - LB3, элементу отображения «Включение/выключение бита по СМС-команде» - LB50, элементу «Настройка модема прошла успешно» - LW18 (тип данных «строка», количество символов — 11, кодировки ASCII и Unicode).

1.2.3 Листинг кода макроса с комментариями

Макрос решает следующие задачи:

1. Распознавание внешнего признака отправки SMS в виде битовой переменной памяти панели, в данном примере реализовано 10 признаков.

2. Запуск алгоритма отправки SMS по одному из внешних признаков. Если в один момент времени активно несколько признаков, они будут отработаны по очереди. СМС отправляется в текстовом режиме (латиница, кодировка ASCII). в СМС вставляется заголовок (задан по умолчанию в теле макроса) и 2 переменные из байтовых регистров памяти панели.

3. Распознавание получения нового СМС.

5. Выполнение одной из двух команд, заложенных в СМС.

Подробный листинг кода макроса.

// Строки 2...5: вызов внешних стандартных библиотек, требуемых для выполнения данного макроса
#include 
#include 
#include 
#include 

// Строка 8: вызов основного метода макроса (является обязательным)
int MacroMain()
{

/* Строки 17...23: блок объявления внутренних переменных макроса
  i, j, switch_byte – переменные вспомогательных циклов и оператора «switch»;
  send_sms_last, CR, LF – переменные, содержащие специальные символы («1А», возврат каретки, перевод строки) для коммуникации с модемом;
  alarm, alarm_ff, done — битовые переменные признаков необходимости отправки (alarm), 
  фиксации получения признака необходимости отправки (alarm_ff), признак завершённой отправки (done);
  *send_baud, *send_format, *send_echo, *send_sms_notify, *send_store, *send_command, send_number[10], 
  number_input[20], *read_command, *delete_command — указатели на строковые константы (кроме send_number[10], number_input[20], 
  которые являются массивами строк), используемые для настройки модема;
  *header[10] — заголовок СМС-сообщения, formed_sms[22] — сформированное СМС, записываемое в интерфейс, 
  modem_cond[] - строка состояния модема для экрана визуализации, recv_buff[200] — буфер чтения из интерфейса. */
 short i,j,switch_byte,send_sms_last[]={26},CR[]={13},LF[]={10};
  bool alarm[10], alarm_ff[10], done[10];
  unsigned char
  *send_baud={"at+ipr=9600"},*send_format={"at+cmgf=1"},*send_echo={"ate0"},*send_sms_notify={"at+cnmi=2,1"},*send_store={"at&w"},*send_command={"at+cmgs=8"}, send_number[10],number_input[20]="",*read_command={"at+cmgr=1"},*delete_command={"at+cmgd=1"},
  *header[10]={"Awariya No1","Awariya No2","Awariya No3","Awariya No4","Awariya No5","Awariya No6","Awariya No7","Awariya No8","Awariya
  No9","Awariya No10"},
  formed_sms[22]="",modem_cond[]="Modem is OK!",recv_buff[200];
// end Блок объявления внутренних переменных

/* Строки 28...50: блок предварительной настройки модема
  конфигурация модема посредством настройки его интерфейса (инструкция SetComParam) 
  и последовательной подачи команд установки скорости обмена, отключения эха, установки текстового формата СМС, 
  включения уведомления о приёме СМС, команды сохранения настроек. */
 int error=SetComParam(1,9600,8,1,110,2); // команда настройки интерфейса: 
 интерфейс COM2, 9600 бод, 8 бит данных, 1 стоп-бит, без проверки чётности, СОМ2 используется как двухпроводный RS-485
 SetWord(@error_com_param@,0,error);
 int error_outport=Outport(1,send_baud,strlen(send_baud)); // команда настройки скорости модема - в данном примере 9600 бод
 error_outport=Outport(1,CR,1); // команда возврата каретки
 error_outport=Outport(1,LF,1); // команда перевода строки
 Delay(50);
 error_outport=Outport(1,send_echo,strlen(send_echo)); // команда включения/выключения эха модема - в данном примере эхо выключено
 error_outport=Outport(1,CR,1);
 error_outport=Outport(1,LF,1);
 Delay(50);
 error_outport=Outport(1,send_format,strlen(send_format)); // команда выбора формата отправки СМС - в данном примере текстовый формат
 error_outport=Outport(1,CR,1);
 error_outport=Outport(1,LF,1);
 Delay(50);
 error_outport=Outport(1,send_sms_notify,strlen(send_sms_notify)); // команда включения уведомления о приёме СМС - в данном примере уведомление включено
 error_outport=Outport(1,CR,1);
 error_outport=Outport(1,LF,1);
 Delay(50);
 error_outport=Outport(1,send_store,strlen(send_store)); // команда включения сохранения настроек модема
 error_outport=Outport(1,CR,1);
 error_outport=Outport(1,LF,1);
 SetWord(@error_outport@,0,error_outport);
 Delay(50);
// end блок предварительной настройки модема

// Оба блока выполняются однократно при загрузке панели

// Строка 56: инициализация бесконечного цикла while(1) для выполнения инструкций, расположенных в теле цикла, каждый цикл панели оператора. Все строки, расположенные выше строки 55, выполняются 1 раз при подаче питания.
 while(1)
 {
// Строка 59: опрос интерфейса модема каждый цикл панели оператора при помощи стандартной инструкции Inport. в буфер приёма читается 200 байтов
 int data_count=Inport(1,recv_buff,200,100); // приём данных из интерфейса каждый цикл панели
// Строки 61...74: проверка содержимого буфера приёма на наличие символов 'OK' как признака получения настроечных команд модемом; 
проверка содержимого буфера приёма на наличие символов 'CMTI' как признака получения нового СМС; 
опрос состояния признака требуемой отправки СМС (alarm[i]) и установка вспомогательных флагов в соответствии с текущим состоянием. 
 for(i=0;i<23;i++)
  {
   if(recv_buff[14]=='O'&&recv_buff[15]=='K') // если полученная строка содержит последовательные символы 'OK' в определённом месте, на экран панели выводится признак 'Modem is OK'
    {SetWord(@modemok@,i,modem_cond[i]);}
   if(recv_buff[3]=='C'&&recv_buff[4]=='M'&&recv_buff[5]=='T'&&recv_buff[6]=='I') // если полученная строка содержит последовательные символы 'CMTI', то начинается последовательность чтения СМС
    {switch_byte=33;}
   if(i<10)
    {
     alarm[i]=GetBit(@Imm_alarm@,i); // приём признака отправки СМС
     if(alarm[j]&!alarm_ff[j]){alarm_ff[j]=1;} // если признак отправки СМС с данным номером появился впервые, то это событие фиксируется
     if(!alarm[j]){alarm_ff[j]=0;done[j]=0;} // если нет признака отправки СМС, то флаги фиксации и окончания посыла сбрасываются
     if(alarm_ff[j]&!done[j]){switch_byte=2;} // если есть признак отправки СМС, но он не отработан, то начинается последовательность отправки СМС
    }
  }
// end проверка содержимого буфера приёма

// Строка 78: вызов оператора switch с выражением switch_byte для реализации последовательности приёма-отправки СМС  
 switch(switch_byte)
  {
   
   // Строки 82...86: заполнение строкового массива send_number символами, введёнными с экрана визуализации панели (номер телефона); формирование буфера отправки number_input конкатенацией строк команды отправки СМС и номера телефона
   case 2:
    {
     for(i=0;i<20;i++){send_number[i]=GetWord(@number_input@,i);} // номер телефона, введённый с экрана панели, из строки преобразуется в последовательность символов
     sprintf(number_input,"%s%s",send_command,send_number); // в буфер обмена записывается команда отправки СМС с номером телефона
    }
   // end заполнение строкового массива
   
   // Строки 90...97: запись буфера отправки и двух специальных символов отправки СМС в интерфейс модема при помощи инструкции Outport, ожидание в течении 3 секунд для приёма ответа от модема
   case 3:
    {
     error_outport=Outport(1,number_input,strlen(number_input)); // запись команды отправки СМС в интерфейс
     error_outport=Outport(1,CR,1); // команда возврата каретки
     error_outport=Outport(1,LF,1);SetWord(@error_outport@,0,error_outport); // команда перевода строки
     Delay(3000); // задержка для ожидания ответа модема о готовности ввода СМС
     switch_byte=4;
    }
   // end запись буфера отправки
   
   // Строки 101...108: очистка буфера приёма перед его заполнением, чтение 12 байтов из интерфейса модема, поиск в буфере приёма последовательности '> ' для продолжения алгоритма отправки — ввода текста сообщения
   case 4:
    {
     for(i=0;i<12;i++){recv_buff[i]=0;} // очищение буфера приёма перед чтением интерфейса
     data_count=Inport(1,recv_buff,12,100); // чтение 12 байтов из интерфейса
     SetWord(@error_inport@,0,data_count); // установка признака ошибки или количества прочитанных байтов
     Delay(1000);
     if(recv_buff[2]==62&&recv_buff[3]==32) {switch_byte=5;}else{switch_byte=100;break;} // если в буфер принята последовательность '> ', то переход к следующему шагу, иначе стоп и выход из цикла отправки
    }
   // end очистка буфера
   
   // Строки 112...132: запись заголовка СМС-сообщения и формирование основного текста исходя из номера события. 
   в данном примере различные тексты СМС формируются для групп событий 1..3, 4..6, 7..8, 9..10. Для группы 4..6 СМС состоит только из заголовка. 
   Стандартная инструкция sprintf формирует строковый массив «formed_sms» для записи в интерфейс из строки-константы в кавычках “symbols%f symbols %d” 
   (специальны символы «%f» и «%d» используются для подстановки в этой позиции строки переменных из следующих аргументов инструкции sprintf), 
   а также из произвольного количества переменных (в любом формате), записываемых через запятую внутри скобок инструкции sprintf. 
   В данном примере в одно СМС записывается максимально 2 переменных из памяти панели оператора из регистров LW16 и LW17. 
   Переменная LW16 преобразуется в число с плавающей точкой при помощи инструкции «(float)» и записывается в строку по месту символа «1.2%f». 
   Переменная LW17 используется как целое число и подставляется в строку по месту символа «%d». 
   Окончательный вид строки в СМС - «D=5.26 bar, Dgaz=49 kPa» (в случае, если переменные LW16 и LW17 будут содержать 526 и 49 соответственно).
   case 5:
    {
	 error_outport=Outport(1,header[j],strlen(header[j])); // запись заголовка СМС сообщения
	 error_outport=Outport(1,LF,1); // команда перевода строки
	 Delay(1000);
	 if(j>=0&j<=2){ // если произошло событие с 1 по 3, то
	   sprintf(formed_sms,"D=%1.2f bar, Dgaz=%d kPa",((float)(GetWord(@Pressure@,0)))/100,GetWord(@Pressure@,1)); 
     // записываемое значение складывается из строки и двух переменных, 
     записываемых в строку по месту символов '%f' (для float) и '%d' (для short)
	   error_outport=Outport(1,formed_sms,strlen(formed_sms)); // количество записанных данных
	   SetWord(@error_outport@,0,error_outport);
	 }
	 if(j==6|j==7){ // если произошло событие 7 или 8, то
       sprintf(formed_sms,"D=%1.2f bar",((float)(GetWord(@Pressure@,j)))/100); // записываемое значение складывается из строки и одной переменной, записываемой в строку по месту символа '%f' (для float)
	   error_outport=Outport(1,formed_sms,strlen(formed_sms));SetWord(@error_outport@,0,error_outport);
	 }
	 if(j==8|j==9){ // если произошло событие 9 или 10, то
	   sprintf(formed_sms,"Dgaz=%d kPa",GetWord(@Pressure@,j+1)); // записываемое значение складывается из строки и одной переменной, записываемой в строку по месту символа '%d' (для short)
	   error_outport=Outport(1,formed_sms,strlen(formed_sms));SetWord(@error_outport@,0,error_outport);
	 }
	 Delay(10);
	 switch_byte=6;
    }
   // end запись заголовка СМС-сообщения
   
   // Строки 145...154: запись символа окончания ввода СМС «1A». 
   После записи этого символа модем начнёт процедуру отправки СМС. 
   В строке 109 вызывается функция задержки с временным интервалом 4 секунды для ожидания ответа о статусе отправки от модема
   case 6:
    {
	 error_outport=Outport(1,send_sms_last,1);SetWord(@error_outport@,0,error_outport); // запись признака окончания СМС - символа '1A'
	 Delay(4000); // задержка для ожидания ответа об отправке СМС
	 switch_byte=7;
    }
   // end запись символа окончания ввода СМС
   
   // Строки 145...154: очистка буфера приёма перед его заполнением, чтение 100 байтов из интерфейса модема, анализ полученных данных. 
   В случае, если в любом месте строки принята последовательность '+CM', устанавливается признак завершения отправки СМС по данному номеру события и производится выход из цикла отправки (инструкция «break»). 
   Иначе если принята последовательность 'ERR', то производится выход из цикла отправки (инструкция «break») без установки признака о завершении. 
   Таким образом в следующем цикле макроса процедура отправки начнётся заново
   case 7:
    {
	 for(i=0;i<100;i++){recv_buff[i]=0;} // очищение буфера приёма перед чтением интерфейса
	 data_count=Inport(1,recv_buff,100,100); // чтение 100 байтов из интерфейса
	 SetWord(@error_inport@,0,data_count);
	 Delay(100);
	 if(recv_buff[2]=='+'&recv_buff[3]=='C'&recv_buff[4]=='M'){switch_byte=0;done[j]=1;} // если в буфер принята последовательность 'ERR', 
   то возврат в начало цикла, иначе стоп и выход из цикла отправки с установкой признака отправки
	 if(recv_buff[2]==69&recv_buff[3]==82&recv_buff[4]==82){;}
	 break;
    }
   // end чистка буфера приёма перед его заполнением
   
   // Строки 158...165: запись команды чтения СМС в интерфейс
   case 33:
    {
	 error_outport=Outport(1,read_command,strlen(read_command)); // запись команды чтения СМС
	 error_outport=Outport(1,CR,1);
	 error_outport=Outport(1,LF,1);SetWord(@error_outport@,0,error_outport);
	 Delay(1000);
	 switch_byte=34;
	}   
   // end запись команды чтения СМС в интерфейс
   
   // Строки 169...183: очистка буфера приёма перед его заполнением, чтение 200 байтов из интерфейса модема, анализ полученных данных. 
   В случае, если в любом месте строки принята последовательность 'run', устанавливается бит LB50 в памяти панели. Иначе если принята последовательность 'stop', то производится сброс бита LB50. 
   Принятое СМС сохранится под номером «1» в хранилище модема в том случае, если не было ранее полученных СМС
   case 34:
    {
	 for(i=0;i<100;i++){recv_buff[i]=0;} // очищение буфера приёма перед чтением интерфейса
	 data_count=Inport(1,recv_buff,200,1000); // чтение 200 байтов из интерфейса
	 SetWord(@error_inport@,0,data_count);
	 for(i=0;i<200;i++)
	  {
	   if(recv_buff[i-2]=='r'&recv_buff[i-1]=='u'&recv_buff[i]=='n') // если в принятой строке присутствуют символы 'run'
	    {SetBit(@LB50@,0,1);} // то установка бита LB50 в 1
		if(recv_buff[i-3]=='s'&recv_buff[i-2]=='t'&recv_buff[i-1]=='o'&recv_buff[i]=='p') // если в принятой строке присутствуют символы 'stop'
		 {SetBit(@LB50@,0,0);} // то установка бита LB50 в 0
	  }
	  Delay(1000);
	  switch_byte=35;
    }   
   // end запись команды чтения СМС в интерфейс
   
   // Строки 187...194: запись команды удаления первого СМС в хранилище модема. 
   Таким образом следующее принятое СМС также будет сохранено модемом под номером «1» 
   и для работы с хранилищем модема не потребуется дополнительных действий
   case 35:
    {
	 error_outport=Outport(1,delete_command,strlen(delete_command)); // запись команды удаления первого СМС
	 error_outport=Outport(1,CR,1);
	 error_outport=Outport(1,LF,1);SetWord(@error_outport@,0,error_outport);
	 Delay(1000);
	 switch_byte=0;
    }  
   // end запись команды удаления первого СМС в хранилище модема
   
  }
   
  // Строки 200...201: увеличение счётчика событий на «1» в конце каждого цикла и присвоение ему значения «0» в случае, если счётчик равен «10» (всего 10 событий с 0 до 9)
  j+=1; // увеличение счётчика событий каждый цикл панели оператора
  if(j==10){j=0;} // если значение счётчика равно 10, то обнуление счётчика
  // end увеличение счётчика событий
  
  return 0;
 }

Заключение

Данный алгоритм реализован при помощи макросов панелей оператора ELHART на языке С. Алгоритм показывает возможности операторских панелей ELHART по прямой работе со встроенными интерфейсами и может использоваться не только как пример работы с GSM-модемами, но и с другими устройствами, имеющими нестандартные протоколы связи.

В алгоритме (в качестве примера) показаны способы чтения и записи произвольных последовательностей символов в интерфейс, а также работа с такими типами данных, как указатели на строки и строковые массивы данных.

Инженер ООО «КИП-Сервис»
Петров Н.П.


 Наверх