Bitcord ct 2 05 инструкция


Описание

Полностью Российское производство. Импортозамещение.

Описание изделия Bitcord CT-2-05.

Модем «Bitcord CT-2-05» – это компактный модем для передачи данных, текстовых сообщений SMS и факсов в сетях сотовой связи поколений 2G/2.5G GSM, создан на основе модуля GL865-DUAL V3.1 Telit. Модем содержит широкий набор периферии – интерфейсы RS232, RS485 без гальванической изоляции, дискретные порты ввода GP-input 2шт, дискретные порты вывода GP-output 2шт. выхода типа открытый коллектор (ОК).

Встроенный условный/безусловный сторожевой таймер обеспечивает как безусловную перезагрузку модема через настраиваемые интервалы времени кратный 1 часу.

Модем может эксплуатироваться в качестве стандартного модема в одном из многочисленных режимов CSD, GPRS TCP/IP (при использовании совместно с ПК или внешним контроллером), SMS, так и исполнять пользовательские сценарии на языке Python, при помощи встроенного интерпретатора.

Области применения.

    • Системы коммерческого учета электроэнергии
    • Системы M2M
    • Системы IoT
    • Удаленная диспетчеризация узлов учета тепла
    • Удаленная диспетчеризация электросчетчиков
    • Удаленная диспетчеризация узлов учета газа
    • Системы безопасности
    • Дистанционный контроль
    • Дистанционные измерения
    • Доступ в Интернет

Характеристики изделия.

Диапазоны частот и стандарты сотовой связи.

  • поддержка диапазонов EGSM900/DCS1800;
  • полное соответствие GSM/GPRS protocol stack 3GPP Release 4;
  • выходная мощность 2Вт(EGSM900) и 1Вт(DCS1800/PCS1900);
  • чувствительность: -108dBm (900МГц), -107dBm (1800МГц);

Протоколы передачи данных и стандарты обработки данных, поддерживаемые модемом

  • CSD — V110;
  • AT-команды — 3GPP 27.005, 27.007 и пользовательские Telit AT команды;
  • Управление через удаленные AT-команды (Remote AT commands);
  • Serial port multiplexer 3GPP 27.010;
  • SIM Application Toolkit 3GPP TS 51.014;
  • Профили доступа к SIM;
  • Встроенный TCP/IP стек управляемый через AT-команды;
  • Поддержка технологии повышения чувствительности приема DARP/SAIC;
  • Поддержка технологии Enhanced Measured Report;
  • Поддержка протокола ITU-T V.24 serial link через один из интерфейсов RS232/RS485 с параметрами:
    • Диапазон скоростей передачи данных – от 300 до 115200 бит/сек;
    • Автоматическое определение скорости- до 115200 бит/сек
  • SMS :
    • Cell Broadcast;
    • Text and PDU mode;
    • Concatenated SMS;
    • SMS over GPRS;
    • Обмен сообщениями SMS в режиме point-point;
  • Дополнительные сервисы GSM:
    • Call forwarding;
    • Call barring;
    • Call waiting & call hold;
    • Advice of charge;
    • Calling line identification presentation (CLIP);
    • Calling line identification restriction (CLIR);
    • USSD;
    • Closed user group;
  • Дополнительные функции:
    • Телефонная книга SIM;
    • Фиксированные номера FDN;
    • Часы реального времени Real Time Clock;
    • Управление сигнализацией Alarm management;
    • Поддержка наборов символов IRA, GSM, 8859-1 и UCS2;
    • Определение «глушения»;
    • Встроенный TCP/IP стек, включая следующие протоколы TCP, IP, UDP, SMTP, ICMP и FTP;
    • EASY SCAN – автоматическое сканирование GSM частот, работает также без SIM карты;
  • Интерпретатор языка Python — доступно 0.8Мбайт энергонезависимой памяти и 1Мбайт ОЗУ для пользовательских скриптов;
  • FOTA – обновление прошивки модуля через AT-команды;

Скорости передачи данных по радиоканалу.

  • Скорости передачи данных сотовая связь:
    • GPRS: Class 10 – DL 85.6 kbps/ UL 42.8 kBps;
  • CSD до 9,6 kbps, V110;

Интерфейсы передачи данных и питания.

  • поддержка 2-х SIM карт;
  • держатель SIM-карты N1: лотковый с толкателем;
  • держатель SIM-карты N2: лотковый с толкателем;
  • поддержка типов SIM карт по питающему напряжению: 1.8В/3.0В;
  • разъем питания: 2-х контактный разрывной клеммник с шагом выводов 3.81мм;
  • интерфейс RS232:
    • полное соответствие стандарту RS232C(EIA)/V.24(ITU-T);
    • поддержка сигналов RX/TX/CTS/RTS/DTR/RING/DSR/DTR;
    • разъем DRB-9FA, таблица выводов п.п. 8.3 РЭ;
    • скорость передачи данных до 115200 кбит/с;
  • интерфейс RS485:
    • полное соответствие стандарту ANSI-TIA/(EIA-485-A:1998);
    • максимальная длина линии передачи – 1200;
    • максимальное количество устройств – 32;
    • скорость передачи данных до 115200 кбит/с;
    • RS-485(GND, B-, A+) (3-х-контактный разрывной клеммник с шагом 3.81 мм);

Внимание! Интерфейсы RS232 и RS485 подключены к одному и тому же порту UART модема, поэтому могут работать в режиме разделения времени.

    • GP-in (GP_in_1 = Вывод GPIO_5 GSM модуля, GP_in_2 = Вывод GPIO_6 GSM модуля, GND) – 2 входа типа TTL
        • тип входа — открытый катод диода, анод которого подключен к напряжению 2.8 В через поледовательный резистор 10 кОм;
        • защита входа от статического электричества — до 15 кВ;
        • максимально допустимое входное напряжение — 6 В (ограничено порогом срабатывания защиты от статического электричества);
        • высокий логический уровень — напряжение от 0.8 до 5.5 В;
        • низкий логический уровень — напряжение от 0 до 0.5 В;
        • входной ток:
    • от минус 200 мкА до 0 мкА при входном напряжении от 0 до 2.3 В
    • от 0 мкА до 50 мкА при входном напряжении от 2.3 до 5.5 В
        • GP-out (GP_out_1 = Вывод GPIO_2 GSM модуля, GP_out_2 = Вывод GPIO_3 GSM модуля, GND) – 2 выхода типа ОК (открытый коллектор)
          • тип выхода — открытый коллектор;
          • максимальное коммутируемое напряжение — 60В;
          • максимальный ток нагрузки — 500 мА;

      Примечание. При индуктивной нагрузке (например, при управлении обмоткой реле) необходим внешний защитный диод (параллельно нагрузке в обратном включении).

      Органы индикации.

      Устройство имеет 2 индикатора NET (GPIO_08), TIMER (GPIO_07), которые могут использованы в пользовательских скриптах, написанных на Python.

      • Индикатор NET (красный) – статус сети GSM;
      • Пиктограмма TIMER (желтый) — настройка и контроль таймера перезагрузки;

      Параметры источника питания.

      • диапазон входного напряжения питания : — +8В … + 36В;
      • защита от инверсии питающего постоянного напряжения на разъеме «Uвх» – есть;
      • потребляемый ток от источника питания 12В, в режиме передачи данных CSD или GPRS, не более — 300 мА;
      • кратковременный, потребляемый ток от источника питания в момент инициализации модема: не более 1000 мА;

      Условия эксплуатации и хранения.

      • рабочая температура: -40 ..+ 80°C;
      • температура хранения: -50 ..+ 85°C;
      • относительная влажность — от 5 до 95% RH;
      • максимальная влажность: 95% RH при +40°C;

      IP-класс защиты и конструктивное исполнение.

      • степень защиты по IEC 60529 (DIN 40050, ГОСТ 14254-96): IP40;

      Массо-габаритные характеристики.

      • Габариты : 62.6 x 39.6 x 69,5 мм (ШxВxД) без учета разъемов
      • Масса: 156гр. (153-159 гр.);

      Комплектность.

      • Устройство;
      • Упаковочная коробка;
      • Паспорт;
      • Антенна на магнитном основании с кабелем длиной 2.5-3.0 м. c разъемом SMA (наличие зависит от комплекта).
      • 3-х контактный 3.81мм для подключения интерфейса RS485 — 1 шт;
      • 3-х контактный 3.81мм для подключения GP_in, GP_out -2шт ;
      • 2-х контактный 3.81мм для подключения напряжения питания устройства;
      • Кабель интерфейсный DB9M-DB9F;

      Таблица модификаций.

      Название модели.

      Модем BITCORD CT-2-05./опции/

       

      Наименовании опции

      Описание опции

      BITCORD CT-2-05.

      RS232

      Интерфейс RS232

      BITCORD CT-2-05.

      RS485

      Интерфейс RS485 3-х контактный без

      гальванической изоляции

      BITCORD CT-2-05.

      gpi-x

      Цифровые входы общего

      назначения с указанием кол-ва через дефис

      BITCORD CT-2-05.

      gpook-x

      Цифровые выходы общего назначения каскад открытый коллектор с указанием кол-

      ва через дефис

      BITCORD CT-2-05.

      2ff-x

      Кол-во симкарт типоразмера

      2ff (miniSIM)

      BITCORD CT-2-05.

      ae

      металлический корпус

      BITCORD CT-2-05.

      ac

      Импульсный источник питания 90-240В

      переменного напряжения


      Характеристики

      Канал передачи данных

      CSD

      Встроенная среда разработки

      Python

      Производитель

      Bitcord (SprutNet)


      Программное обеспечение, сертификаты и документация

      ГАРАНТИЯ 48 МЕСЯЦЕВ

      СНЯТ С ПРОИЗВОДСТВА

         Модем «Bitcord CT-2-05» – это компактный модем для передачи данных, текстовых сообщений и факсов в сетях сотовой связи поколений 2G/2.5G GSM, создан на основе модуля GL865-DUAL V3.1 Telit. Модем содержит широкий набор периферии – последовательный порт с двумя совмещенными интерфейсами типа RS232 и RS485, дискретные порты ввода GP-input (2 шт.), дискретные порты вывода GP-output (2 шт. c открытым коллектором), держатели для SIM-карт (2 шт. размера Mini-SIM).

         Встроенный настраиваемый сторожевой таймер обеспечивает безусловную перезагрузку модема через равные интервалы времен от 1 до 24 часов, с шагом настройки 1 час..

         Поддержка соединений CSD и GPRS, сервис обмена сообщениями SMS, расширенный набор AT-команд, встроенный TCP/IP стек и программный интерпретатор языка  Python для исполнения пользовательских сценариев — позволяют использовать модем для решения широкого круга задач как в автономном режиме, так и совместно с ПК или внешним контроллером.

      • Системы коммерческого учета электроэнергии
      • Системы M2M
      • Системы IoT
      • Удаленная диспетчеризация узлов учета тепла
      • Удаленная диспетчеризация электросчетчиков
      • Удаленная диспетчеризация узлов учета газа
      • Системы безопасности
      • Дистанционный контроль
      • Дистанционные измерения
      • Доступ в Интернет
      • Модем
      • Антенна c разъемом SMA;
      • Кабель интерфейсный DB9M-DB9F;
      • Набор клеммников:
        • 2-х контактный 3.81мм, для подключения низковольтного напряжения питания;
        • 3-х контактный 3.81мм для подключения интерфейса RS485;
        • 3-х контактный 3.81мм для подключения интерфейса GP_in;
        • 3-х контактный 3.81мм для подключения интерфейса GP_out;
      • Паспорт изделия;
      • Картонная коробка;

      Диапазоны частот и стандарты сотовой связи.

      • поддержка диапазонов EGSM900/DCS1800;
      • полное соответствие GSM/GPRS protocol stack 3GPP Release 4;
      • выходная мощность 2Вт(EGSM900) и 1Вт(DCS1800/PCS1900);
      • чувствительность: -108dBm (900МГц), -107dBm (1800МГц);

      Протоколы передачи данных и стандарты обработки данных, поддерживаемые модемом

      • Поддержка AT-команд – «3GPP 27.005», «3GPP 27.007», «FAX class 1» и собственные пользовательские AT-команды Telit;
      • Поддержка удаленного управления через SMS c помощью AT-команды (Remote AT commands);
      • Поддержка протокола мультиплексирования «3GPP 27.010» для последовательного порта;
      • Поддержка инструментария «SIM Application Toolkit» согласно протокола «3GPP TS 51.014»;
      • Поддержка профилей доступа к SIM;
      • Встроенный TCP/IP стек управляемый через AT-команды;
      • Поддержка технологии повышения чувствительности приема DARP/SAIC;
      • Поддержка технологии Enhanced Measured Report;
      • Поддержка протокола ITU-T V.24 serial link через один из интерфейсов RS232/RS485 с параметрами:
        • Диапазон скоростей передачи данных – от 300 до 115200 бит/сек;
        • Автоматическое определение скорости- до 115200 бит/сек
      • Поддержка сервиса SMS :
        • Cell Broadcast;
        • Text and PDU mode;
        • Concatenated SMS;
        • SMS over GPRS;
        • Обмен сообщениями SMS в режиме point-point;
      • Поддержка дополнительных сервисы GSM:
        • Call forwarding;;
        • Call barring;;
        • Call waiting & call hold;
        • Advice of charge;
        • Calling line identification presentation (CLIP);
        • Calling line identification restriction (CLIR);
        • USSD;;
        • Closed user group;
      • Дополнительные функции:
        • Телефонная книга SIM;
        • Фиксированные номера FDN;
        • Часы реального времени Real Time Clock;
        • Управление сигнализацией Alarm management;
        • Поддержка наборов символов IRA, GSM, 8859-1 и UCS2;
        • Определение «глушения»;
        • Встроенный TCP/IP стек, включая следующие протоколы TCP, IP, UDP, SMTP, ICMP и FTP;
        • EASY SCAN – автоматическое сканирование GSM частот, работает также без SIM карты;
      • Интерпретатор языка Python — доступно 0.8Мбайт энергонезависимой памяти и 1Мбайт ОЗУ для пользовательских скриптов;
      • FOTA – обновление прошивки модуля через AT-команды;

      Скорости передачи данных по радиоканалу.

      • Скорости передачи данных сотовая связь:
        • GPRS: Class 10 – DL 85.6 kbps/ UL 42.8 kBps;
      • CSD до 14,4 kbps

      Интерфейсы передачи данных и питания.

      • Два держателя для SIM карт;
        • держатель SIM-карты #1: лотковый с толкателем;
        • держатель SIM-карты #2: лотковый с толкателем;
        • поддержка типов SIM карт по питающему напряжению: 1.8В/3.0В;
      • разъем питания: 2-х контактный разрывной клеммник с шагом выводов 3.81мм;
      • интерфейс RS232:
        • полное соответствие стандарту RS232C(EIA)/V.24(ITU-T);
        • поддержка сигналов RX/TX/CTS/RTS/DTR/RING/DSR/DTR;
        • разъем DRB-9FA, таблица выводов п.п. 8.3 РЭ;
        • скорость передачи данных до 115200 кбит/с;
      • интерфейс RS485:
        • полное соответствие стандарту ANSI-TIA/(EIA-485-A:1998);
        • максимальная длина линии передачи – 1200;
        • максимальное количество устройств – 32;
        • скорость передачи данных до 115200 кбит/с;
        • RS-485(GND, B-, A+) (3-х-контактный разрывной клеммник с шагом 3.81 мм), таблица выводов п.п. 8.2 РЭ;
        • Внимание! Интерфейсы RS232 и RS485 подключены к одному и тому же последовательному порту ASC0 модуля GSM, поэтому должны использоваться только в режиме разделения по времени.

      • дискретные входы GP IN:
        • вход 1 управляет выводом GPIO_05 модуля GSM
        • вход 2 управляет выводом GPIO_06 модуля GSM
        • тип входов — открытый эмиттер NPN-транзистора, база которого подключена к напряжению +3.86 В через сопротивление 10 кОм
        • возможность прямого подключения «сухих» контактов
        • защита входов от статического электричества — до 15 кВ
        • максимально допустимое входное напряжение +6 В (ограничено порогом срабатывания защиты от статического электричества)
        • высокий логический уровень — напряжение от +1.3 до +5.5 В
        • низкий логический уровень — напряжение от 0 до +0.35 В
        • входной ток:
          • минус 360 мкА при входном напряжении от 0 В
          • 100 нА при входном напряжении от +3.2 до +5.5 В
      • дискретные выходы GP OUT:
        • выход 1 управляется выводом GPIO_02 модуля GSM
        • выход 2 управляется выводом GPIO_03 модуля GSM
        • тип выхода — открытый коллектор NPN-транзистора. Логическая единица на управляющем выводе модуля GSM соответствует открытому состоянию транзистора
        • максимальное коммутируемое напряжение +60В;
        • максимальный ток нагрузки 500 мА;
        • Примечание. При индуктивной нагрузке (например, при управлении обмоткой реле) необходим внешний защитный диод (параллельно нагрузке в обратном включении).

      Органы индикации.

      Устройство имеет 2 дискретных светодиодных индикатора NET и TIMER, которые могут использованы в пользовательских скриптах, написанных на Python.

      • Индикатор NET (красный цвет):
        • управляется выводом GPIO_08/STAT_LED модуля GSM. Логическая единица на управляющем выводе модуля GSM включает индикатор
        • функция по-умолчанию — статус сети GSM
      • Индикатор TIMER (желтый цвет):
        • подключен к выводу микроконтроллера, отвечающего за работу таймера перезагрузки
        • режим работы по-умолчанию — настройка и контроль таймера перезагрузки
        • дополнительный режим — отображение состояния вывода GPIO_07 модуля GSM. Логический ноль на управляющем выводе модуля GSM включает индикатор

      Параметры источника питания

      • диапазон входного напряжения питания : — +8В … + 36В постоянного тока;
      • защита от инверсии питающего постоянного напряжения на разъеме «Uвх» – есть;
      • потребляемый ток от источника питания 12В, в режиме передачи данных CSD или GPRS, не более — 300 мА;
      • кратковременный, потребляемый ток от источника питания в момент инициализации модема: не более 1000 мА;

      Условия эксплуатации и хранения.

      • рабочая температура: -40 ..+ 80°C;
      • температура хранения: -50 ..+ 85°C;
      • относительная влажность — от 5 до 95% RH;
      • максимальная влажность: 95% RH при +40°C;

      IP-класс защиты и конструктивное исполнение.

      • степень защиты по IEC 60529 (DIN 40050, ГОСТ 14254-96): IP40;

      Массо-габаритные характеристики.

      • Габариты : 62.6 x 39.6 x 69,5 мм (ШxВxД) без учета разъемов
      • Масса: 156гр. (153-159 гр.);

      Модем может поставляться со специальным скриптом на языке Python, который превращает его в мощный терминал, работающий в режиме клиент-сервер по протоколу TCP/IP.

      

      Radio Life — Работа с GPRS модемом Telit

      Работа с GPRS модемом Bitcord СТ-2-05

      1. С чего начать.

      GPRS-модем Bitcord СТ2-05 представляет из себя небольшую коробочку с двумя слотами для сим-карт, индикаторами TIMER и NET и набором разъемов. На начальном этапе работы требуются три из них — разъем питания, RS232 и антенный разъем.

      Рис. 1. Внешний вид модема

      Задача, собственно, очень простая — обеспечить соединение модема с сервером TELNET и организовать прием/передачу данных через порт RS232 с помощью любой терминальной программы (Hyper Teminal, Putty и т. п.).

      Соединение будем устанавливать через Мегафон с фиксированным IP.

      Естественно, начать надо с того, чтобы вставить в слот для SIM1 сим-карту, присоединить антенну, затем подключить питание. Должен несколько раз мигнуть, затем загореться зеленый индикатор TIMER. В процессе работы он будет «подмигивать» с некоторым интервалом, который зависит от установленного времени таймера перезапуска модема.

      Частое мигание индикатора NET показывает, что соединения с оператором нет. Если он не переходит в режим «помаргивания» с интервалом примерно 3 сек., то надо проверить антенну, сим-карту и вообще наличие соответствующего оператора в данном месте.

      Конечно, требуется компьютер с нормальным 9-пиновым COM-портом. Если имеется только USB, то можно использовать преобразователь USB-COM.

      Порт RS232 модема по умолчанию установлен в режим автоматического определения скорости передачи, поэтому на компьютере можно установить скорость передачи любую. Я поставил 115200, 8 бит, 1 стоповый.

      Подключаем кабель модема к порту компьютера и приступаем к настройке.

      2. Информация о модели, сброс к «заводским» настройкам

      Сначала надо проверить «доступность» модема. Набираем команду AT:

      
      AT
      OK
      

      Если получили ответ OK, можно продолжать. Для начала определим версию модели и прошивки. Можно заметить, что команды модему можно набирать как прописными, так и строчными буквами:

      
      at+cgmm (модель)
      GL865-DUAL-V3.1
      OK
      at+cgmr (прошивка)
      16.01.173
      OK
      AT+GMI (информация о производителе)
      telit
      OK
      

      В процессе работы может потребоваться сбросить модем к начальным установкам. Обнаружить, что сбрасывает стандартная команда AT&F0(1) не удалось (похоже, что ничего). Поэтому приходится использовать следующие команды:

      
      at+cfun=4
      OK
      at+cmar=00000000
      OK
      

      Команда AT+CFUN=4 деактивирует SIM-карту и сбрасывает сетевое соединение. Команда AT+CMAR=00000000 сбрасывает данные пользователя. 00000000 — «секретный код». Возможно, на других модемах он может быть иным, но производитель данного устройства указал для сброса такой код.

      При этом форматируется память, поэтому все пользовательские скрипты в модеме также будут стерты. После сброса перестает работать индикатор NET, поэтому необходимо ввести следующие команды:

      
      at#sled=2,10,10
      OK
      at#sledsav
      OK
      at&w
      OK
      

      3. Настройка сетевых параметров

      Прежде всего — проверим установки и подключение SIM-карты:

      
      at#qss?    (запрос статуса SIM-карты)
      #QSS: 0,1  (0 — запрос статуса по команде QSS, 1 — SIM-карта вставлена)
      OK
      at+cpin?      (запрос готовности PIN-кода)
      +CPIN: READY  (ввод PIN-кода не требуется)
      OK
      at+cgreg?     (запрос статуса GPRS)
      +CGREG: 0,1   (0 — отключен код регистрации в сети, 1 — модем зарегистрирован в сети)
      OK
      at+cops?      (запрос имени оператора)
      +COPS: 0,0,"MegaFon RUS"
      OK
      

      Итак, SIM-карта подключилась к нужному оператору (в нашем случае — Мегафон) и теперь можно перейти к настройкам собственно соединения. Сначала проверим установки параметров сокетов:

      
      at#scfg?
      
      #SCFG: 1,1,300,90,600,50
      #SCFG: 2,1,300,90,600,50
      #SCFG: 3,1,300,90,600,50
      #SCFG: 4,2,300,90,600,50
      #SCFG: 5,2,300,90,600,50
      #SCFG: 6,2,300,90,600,50
      

      Команда выводит параметры для 6 возможных сокетов и двух (из 5 возможных) наборов контекстов PDP (Packet Data Protocol type). Нам требуется всего один сокет (для telnet-соединения), выберем для работы сокет 1 и контекст 1. Рассмотрим по порядку, что означают эти цифры для первого сокета.

      1 — номер сокета; 1 — номер контекста PDP, который используется для этого сокета; 300 — размер пакета данных, передаваемых по TCP-соединению; 90 — таймаут ожидания сброса соединения при отсутствии активности (в секундах); 600 — таймаут ожидания установки соединения (в сотнях миллисекунд); 50 — таймаут передачи данных (в сотнях миллисекунд), в случае, если пакет для передачи меньше размера пакета (в данном случае — 300 байт).

      Подробнее о значении этих параметров, возможно, скажу ниже, а пока можно оставить их «как есть».

      Теперь настроим параметры контекста соединения. Это делает команда CGDCONT:

      
      at+cgdcont=1,"IP","FixedIP.nw"
      

      Здесь: 1 — идентификатор контекста; IP — тип протокола передачи данных (интернет протокол); FixedIP.nw — имя точки доступа APN, зависит от оператора и им же должно сообщаться. В нашем случае используется фиксированный IP Мегафона. Команда CGDCONT имеет еще ряд параметров, но для нашего случая достаточно и этих (остальные отсавим по умолчанию), поэтому ими и ограничимся. Значения всех параметров контекста можно посмотреть той же командой:

      
      at+cgdcont?
      +CGDCONT: 1,"IP","FixedIP.nw","",0,0
      OK
      

      У нас настроен только один контекст, поэтому выводится только одна строка. При настройке нескольких контекстов PDP будет столько строк, сколько есть настроенных контекстов.

      Приведенные выше настройки сохраняются в памяти модема и при отключении питания, поэтому их достаточно ввести только один раз.

      4. Установка соединения

      Перед установкой соединения с сервером telnet еще раз проверим наличие соединения с сетевым оператором командой CREG:

      
      at+creg?
      +CREG: 0,1
      OK
      

      Результат аналогичен описанной выше команде CGREG.

      Далее активируем собственно контекст командой SGACT:

       
      at#sgact=1,1
      #SGACT: 100.000.000.000
      OK
      

      В строке команды можно дополнительно указать пользователя и пароль, но в нашем случае этого не требуется. При успешном завершении команда выводит IP адрес модема, который в случае SIM-карты со статическим адресом будет всегда один и тот же.

      И, наконец, устанавливаем telnet соединение с сервером с помощью команды SD:

      
      at#sd=1,0,23,200.200.100.100
      CONNECT
      œœ œ#œ'
      

      1 — номер сокета; 0 — протокол передачи TCP; 23 — номер порта; 200.200.100.100 — адрес сервера.

      Возможно, в большинстве случаев приведенного выше набора команд для работы будет достаточно, однако в нашем случае сервер не только ответил загадочным набором символов, но и категорически отказывается воспринимать ввод с клавиатуры. Можно добавить, что в случае бездействия, через интервал, установленный командой SCFG (при указанных выше настройках этот таймаут равен 90 сек) соединение будет сброшено, и модем выведет сообщение NO CARRIER. В любом случае желательно решить две проблемы:

      1. Набирать указанные команды при каждом подключении не очень удобно — всегда есть вероятность ошибки.

      2. Для установки соединения с нашим сервером необходимо сделать еще что-то, чтобы обеспечить возможность обмениваться данными.

      Для решения этих задач придется обратиться к скриптам, которые будем писать на языке Python, интерпретатор которого встроен в данный модем.

      5. Подготовка к работе со скриптами

      Для разработки скриптов необходим следующий минимум программного обеспечения:

      1. Любой текстовой редактор. Я пользуюсь tsWebEditor.

      2. Терминальный клиент HiperTerminal. Выбор этой программы обусловлен тем, что с ее помощью можно загружать в модем текстовые неоткомпилированные скрипты. Настройки программы для загрузки приведены в инструкции telit_easy_script_python_r18.pdf и будут приведены ниже.

      3. Для сборки скриптов нужен Telit Python package (TelitPy1.5.2+_v4.1.exe), который содержит необходимые библиотеки.

      4. Для загрузки в модем откомпилированных скриптов желателен Rsterm.

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

      6. Простой скрипт

      Перейдем к практике. Сначала напишем самый простой скрипт, который будет автоматически запускаться при включении питания модема (или по команде с терминала).

      Для общения с внешним миром в модемы Telit встроен ряд программных модулей, которые и будем использовать. Эти модули следующие:

      MOD — некоторый набор функций, имеющих отношение к работе самого модема.

      MDM — интерфейс, который отвечает за обмен командами и данными с внешним миром.

      SER — интерфейс последовательного порта, через который происходит прием/передача данных.

      Пока достаточно этих модулей, что будет видно из текста программы. Для того, чтобы использовать эти модули, их необходимо «подключить», для чего в текст программы надо вставить следующие строки:

      
      import MOD
      import MDM
      import SER
      

      После подключения необходимых модулей, пишем собственно скрипт:

      
      SER.send('GPRS CONTEXT activated ... rn')
      MDM.send('AT#SGACT=1,1r', 10)
      MOD.sleep(20)
      SER.send('GPRS Connect ...rn')
      MOD.sleep(20)
      MDM.send('AT#SD=1,0,23,200.200.100.100r', 10)
      

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

      MOD.sleep(20): простая задержка, выражающаяся в сотнях миллисекунд. Нужна для того, чтобы дать как модему, так и оператору время на выполнение операции.

      SER.send(‘GPRS CONTEXT activated … rn’): команда вывода на COM-порт информационного сообщения, чтобы мы могли хотя бы знать, что скрипт запустился.

      MDM.send(‘AT#SGACT=1,1r’, 10): описанная выше команда активации контекста PDP.

      MDM.send(‘AT#SD=1,0,23,200.200.100.100r’, 10): установка соединения с сервером.

      Полный текст скрипта получается такой:

      
      import MOD
      import MDM
      import SER
      
      SER.send('GPRS CONTEXT activated ... rn')
      MDM.send('AT#SGACT=1,1r', 10)
      MOD.sleep(20)
      SER.send('GPRS Connect ...rn')
      MOD.sleep(20)
      MDM.send('AT#SD=1,0,23,200.200.100.100r', 10)
      

      Сохраняем его в виде текстового файла с расширением«.py», например, test.py, и перейдем к процессу загрузки и запуска.

      Рис. 2. Настройки параметров COM-порта

      Прежде всего, приведем настройки HyperTerminal’а в соответствие с рекомендациями Telit. Рекомендованные настройки показаны на рис. 2—4.

      Рис. 3. Свойства терминала

      Рис. 4. Настройки параметров ASCII

      Передача данных в модем производится в режиме отправки текстового файла (см. рис. 5). Для этого вводится следующая команда:

      
      at#wscript="test.py",227
      
      >>>
      

      Где test.py — имя файла, а 227 — его размер. Естественно, у вас может быть и другой размер, в зависимости от оформления текста скрипта.

      Рис. 5. Загрузка файла в модем.

      После появления на экране терминала приглашения к передаче (>>>), следует успеть перейти к передаче файла. Обратите внимание, что для того, чтобы иметь возможность указать терминалу имя передаваемого файла в окошке Тип файлов следует указать Все файлы (*.*).

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

      
      at#lscript
      #LSCRIPT: "test.py",227
      
      OK
      

      Далее настроим режим запуска скрипта. Это можно сделать с помощью команды STARTMODESCR, применив ее следующим образом:

      
      at#startmodescr=1,30
      
      OK
      

      Первый параметр определяет режим запуска скрипта: 0 — запуск зависит от уровня сигнала DTR. На мой взгляд, это не совсем удобно. 1 — запуск скрипта произойдет автоматически через интервал, указанный во втором параметре команды (в нашем случае — 30 сек.), при условии, что в течение этого интервала не будет введена команда через порт RS232. Мне больше нравится этот вариант, т. к. он позволяет надежно остановить запуск скрипта, что вполне может понадобится при отладке.

      Теперь надо указать модему, какой скрипт следует запустить (ведь скриптов в модем можно записать не один — памяти для этого достаточно). Это устанавливается следующей командой:

      
      at#escript="test.py"
      
      OK
      at#escript?
      #escript="test.py"
      
      OK
      

      В заключение добавлю, что если по каким-то причинам вы остановили запуск скрипта вводом команды через порт RS232, то его при необходимости можно запустить «вручную»:

      
      at#execscr
      
      OK
      

      При этом будет запущен скрипт, ранее указанный командой ESCRIPT.

      Теперь можно перезапустить модем (например, выключив и включив питание) и посмотреть что у нас получилось. В общем, при нормальных условиях, на экране терминала, подключенного к модему, должно появиться примерно следующее: FF FD 18 FF FD 20 FF FD 23 FF FD 27 (я перевел «непечатные» символы в 16-ричный код, чтобы дальнейшее было понятнее).

      Что с этим делать, тем более, что, повторю, никакого отклика на нажатие клавиш на терминале не происходит?

      7. Работа с командами IAC

      Дело в том, что в протоколе telnet предусмотрен запрос сервером у клиента параметров соединения или самого клиента. Обмен производится с помощью команд, предваряемых кодом IAC (Interpret As Command — интерпретировать как команду, код 0xFF).

      Полный список команд довольно длинный, однако в нашем случае достаточно обойтись следующим набором:

      
      IAC =  0xFF
      
      DONT = 0xFE
      DO =   0xFD
      WONT = 0xFC
      WILL = 0xFB
      SB =   0xFA
      AYT =  0xF6
      SE =   0xF0
      
      TELOPT_ECHO =        0x01
      TELOPT_SGA =         0x03
      TELOPT_STATUS =      0x05
      TELOPT_SUPDUP =      0x15
      TELOPT_TTYPE =       0x18
      TELOPT_NAWS =        0x1F
      TELOPT_TSPEED =      0x20
      TELOPT_LFLOW =       0x21
      TELOPT_XDISPLOC =    0x23
      TELOPT_NEW_ENVIRON = 0x27
      
      TELQUAL_IS =   0x00
      TELQUAL_SEND = 0x01
      

      Теперь можно «расшифровать» запрос сервера: WILL TTYPE — запрос типа терминала, WILL TSPEED — запрос скорости терминала, WILL XDISPLOC — что-то вроде запроса местоположения и, наконец, WILL NEW_ENVIRON — запрос переменных среды.

      В большинстве случаев достаточно указать в ответ тип терминала:

      
      #
      #   посылка на сервер команды IAC
      #   -----------------------------
      def SendIAC(cmd, opt):
          s = ('%c%c%c' % (chr(IAC), chr(cmd), chr(opt)))
          MDM.send('%s' % s, 5)
      #
      #   обработка запросов сервера IAC
      #   ------------------------------
      def processIAC (s):
          global echoTln
          global sgaFlg
          global TelqualSend
      
          if (s[1] == chr(IAC)):
              return IAC
          else:
              cmd = ord(s[1])
              opt = ord(s[2])
              SER.sendbyte(ord('.'))
      #       обработка TELOPT_ECHO
              if (opt == TELOPT_ECHO):
                  if (cmd == WILL):
                      if (echoTln == 1):
                          echoTln = 0
                          SendIAC(DO, TELOPT_ECHO)
                  else:
                      if (cmd == WONT):
                          if(echoTln == 0):
                              echoTln = 1
                              SendIAC(DONT, TELOPT_ECHO)
                      else:
                          if (cmd == DO):
                              echoTln = 0
                              so = ('%c%c%c%c%c%c' % (chr(IAC),chr(WONT),chr(TELOPT_ECHO),chr(IAC),chr(DO),chr(TELOPT_ECHO)))
                              MDM.send('%s' % so, 5)
                          else:
                              if (cmd == DONT):
                                  echoTln = 0
                                  SendIAC(WONT, TELOPT_ECHO)
      #       обработка TELOPT_SGA
              else:
                  if (opt == TELOPT_SGA):
                      if (cmd == WONT):
                          sgaFlg = 1
                          if (echoTln == 0):
                              echoTln = 1
                              SendIAC(DONT, TELOPT_SGA)
                      else:
                          if (cmd == WILL):
                              sgaFlg = 0
                              if (echoTln != 0):
                                  so = ('%c%c%c%c%c%c' % (chr(IAC),chr(DO),chr(TELOPT_SGA),chr(IAC),chr(DO),chr(TELOPT_ECHO)))
                                  MDM.send('%s' % so, 5)
      #           обработка TELOPT_TTYPE
                  else:
                      if (opt == TELOPT_TTYPE):
                          if (cmd == DO):
                              SendIAC(WILL, TELOPT_TTYPE)
                          else:
                              if (cmd == SB):
                                  n = 0
                                  flag = 0
                                  while (n < 41):
                                      y = s[n+3]
                                      if (y == chr(IAC)):
                                          flag = 1
                                          n = n + 1
                                      else:
                                          if (flag != 0 and y == chr(SE)):
                                              break
                                          else:
                                              flag = 0
                                              n = n + 1
                                  if (flag == 0):
                                      return n + 3
                                  if (s[3] == chr(TELQUAL_SEND)):
                                      so = ('%c%c%c%c%s%c%c' % (chr(IAC),chr(SB),chr(TELOPT_TTYPE),chr(TELQUAL_IS),TermType,chr(IAC),chr(SE)))
                                      MDM.send('%s' % so, 5)
                                      return n+3
      #               обработка других опций
                      else:
                          if (cmd == WILL):
                              SendIAC(DONT, opt)
                          else:
                              if (cmd == DO):
                                  so = ('%c%c%c%c%c%c' % (chr(IAC),chr(WONT),chr(opt),chr(IAC),chr(DONT),chr(opt)))
                                  MDM.send('%s' % so, 5)
                              else:
                                  if (cmd == DONT):
                                      SendIAC(WONT, opt)
          return 3
      

      Это, конечно, далеко не полный обработчик запросов IAC, но для начала работы его вполне достаточно.

      Осталось добавить конфигурационный файл (назвав его, например, settings.ini) в котором можно хранить настройки программы, например, такого вида:

      
      AUTH_TYPE::1::
      APN::FixedIP.nw::
      GPRS_USER::user::
      GPRS_PASSWD::password::
      DEST_IP::200.200.100.100::
      SECONDARY_ID::1234::
      COM_SPEED::57600::
      

      Можно обратить внимание на строчку SECONDARY_ID. Дело в том, что в некоторых случаях сервер может отправлять клиенту запрос для получения дополнительных параметров соединения. Код запроса — 0x05. При ответе на этот запрос клиент может отправить, кроме всего прочего, и собственный идентификатор. Это бывает полезно, если необходимо, например, идентифицировать клиента не только по IP или логину.

      Для работы с файлом конфигурации создадим дополнительный класс Config, записав его в отдельный файл config.py.

      
      class Config:
          def __init__(self):
              self.config = {}
      
          def get(self, k):
              return self.config[k]
      
          def set(self, k, v):
              self.config[k] = v
      
          def read(self):
              try:
                  fh = open("settings.ini", "r")
                  try:
                      lines = fh.readlines()
                      for l in lines:
                          kv = l.strip().split('::')
                          self.config[kv[0]] = kv[1]
                  finally:
                      fh.close()
              except IOError:
                  print "Configuration file not found."
      
          def write(self):
              try:
                  fh = open("settings.ini", "w")
                  try:
                      lines = []
                      for k in self.config.keys():
                          lines.append(k + "::" + self.config[k] + "::rn")
                      fh.writelines(lines)
                  finally:
                      fh.close()
              except IOError:
                  print "Configuration file not found."
      
          def dump(self):
              for k in self.config.keys():
                  print k + "::" + self.config[k]
      

      И, наконец, полный текст программы:

      
      import MOD
      import MDM
      import SER
      import MDM2
      
      import config
      CONFIG = config.Config()
      CONFIG.read()
      
      version = '04.151'
      
      IAC = 255
      
      DONT = 254
      DO = 253
      WONT = 252
      WILL = 251
      SB = 250
      AYT = 246
      SE = 240
      
      TELOPT_ECHO = 1
      TELOPT_SGA = 3
      TELOPT_STATUS = 5
      TELOPT_SUPDUP = 21
      TELOPT_TTYPE = 24
      TELOPT_NAWS = 31
      TELOPT_TSPEED = 32
      TELOPT_LFLOW = 33
      TELOPT_XDISPLOC = 35
      TELOPT_NEW_ENVIRON = 39
      
      TELQUAL_IS = 0
      TELQUAL_SEND = 1
      
      echoTln = 1
      sgaFlg = 1
      exitScript = 0
      restartModem = 0
      secondaryID = 0
      isConnect = 0
      TelqualSend = 0
      TermType = 'VT220'
      TermSpeed = '38400,38400'
      
      autoLogin = 1
      isLogin = 0
      isPassword = 0
      isEscape = 0
      
      Interval = 0L
      numInterval = 0
      DELAY_INT = 850
      IP_address = ''
      
      #
      #   проверка наличия сети
      #   ---------------------
      def checkNetwork():
          SER.send('Try to Find Net ...')
          MOD.sleep(20)
          REC_TIME = 200
          for _ in range(10):
              MDM.send("AT+CREG?r",0)
              res = MDM.receive(REC_TIME)
              if (res.find('0,1')!=-1):
                  SER.send(' OKnr')
                  return 1
              else:
                  MOD.sleep(50)
          SER.send(' Net Not Foundnr')
          return 0
      #
      #   активация контекста (вхождение в сеть)
      #   --------------------------------------
      def activateContext():
          REC_TIME = 200
          s = ''
          SER.send('GPRS CONTEXT setup ... ')
          MDM.send('AT+CGDCONT=1,"IP","' + CONFIG.get('APN') + '"r', 0)
          while(1):
              s = s + MDM.receive(10)
              if(s.find('OK')!=-1):
                  SER.send(' OKnr')
                  break
              if(s.find('ERROR')!=-1):
                  SER.send(' ERRORnr')
                  break
          SER.send('GPRS CONTEXT activated ...')
          MDM.send('AT#SGACT=1,1r', 10)
          MOD.sleep(20)
          s = ''
          while(1):
              s = s + MDM.receive(10)
              if(s.find('OK')!=-1):
                  result = 0
                  break
              if(s.find('ERROR')!=-1):
                  result = -1
                  break
          if (result == 0):
              str_IP = s.split(chr(13))
              i_str_IP = str_IP[1].find(': ')
              My_IP = str_IP[1][i_str_IP + 2:]
              SER.send(' OKnr')
              return My_IP
          else:
              SER.send('ERROR.rn')
              return 0
      #
      #   посылка на сервер команды IAC
      #   -----------------------------
      def SendIAC(cmd, opt):
          s = ('%c%c%c' % (chr(IAC), chr(cmd), chr(opt)))
          MDM.send('%s' % s, 5)
      #
      #   обработка запросов сервера IAC
      #   ------------------------------
      def processIAC (s):
          global echoTln
          global sgaFlg
          global TelqualSend
      
          if (s[1] == chr(IAC)):
              return IAC
          else:
              cmd = ord(s[1])
              opt = ord(s[2])
              SER.sendbyte(ord('.'))
      #       обработка TELOPT_ECHO
              if (opt == TELOPT_ECHO):
                  if (cmd == WILL):
                      if (echoTln == 1):
                          echoTln = 0
                          SendIAC(DO, TELOPT_ECHO)
                  else:
                      if (cmd == WONT):
                          if(echoTln == 0):
                              echoTln = 1
                              SendIAC(DONT, TELOPT_ECHO)
                      else:
                          if (cmd == DO):
                              echoTln = 0
                              so = ('%c%c%c%c%c%c' % (chr(IAC),chr(WONT),chr(TELOPT_ECHO),chr(IAC),chr(DO),chr(TELOPT_ECHO)))
                              MDM.send('%s' % so, 5)
                          else:
                              if (cmd == DONT):
                                  echoTln = 0
                                  SendIAC(WONT, TELOPT_ECHO)
      #       обработка TELOPT_SGA
              else:
                  if (opt == TELOPT_SGA):
                      if (cmd == WONT):
                          sgaFlg = 1
                          if (echoTln == 0):
                              echoTln = 1
                              SendIAC(DONT, TELOPT_SGA)
                      else:
                          if (cmd == WILL):
                              sgaFlg = 0
                              if (echoTln != 0):
                                  so = ('%c%c%c%c%c%c' % (chr(IAC),chr(DO),chr(TELOPT_SGA),chr(IAC),chr(DO),chr(TELOPT_ECHO)))
                                  MDM.send('%s' % so, 5)
      #           обработка TELOPT_TTYPE
                  else:
                      if (opt == TELOPT_TTYPE):
                          if (cmd == DO):
                              SendIAC(WILL, TELOPT_TTYPE)
                          else:
                              if (cmd == SB):
                                  n = 0
                                  flag = 0
                                  while (n < 41):
                                      y = s[n+3]
                                      if (y == chr(IAC)):
                                          flag = 1
                                          n = n + 1
                                      else:
                                          if (flag != 0 and y == chr(SE)):
                                              break
                                          else:
                                              flag = 0
                                              n = n + 1
                                  if (flag == 0):
                                      return n + 3
                                  if (s[3] == chr(TELQUAL_SEND)):
                                      so = ('%c%c%c%c%s%c%c' % (chr(IAC),chr(SB),chr(TELOPT_TTYPE),chr(TELQUAL_IS),TermType,chr(IAC),chr(SE)))
                                      MDM.send('%s' % so, 5)
                                      return n+3
      #               обработка других опций
                      else:
                          if (cmd == WILL):
                              SendIAC(DONT, opt)
                          else:
                              if (cmd == DO):
                                  so = ('%c%c%c%c%c%c' % (chr(IAC),chr(WONT),chr(opt),chr(IAC),chr(DONT),chr(opt)))
                                  MDM.send('%s' % so, 5)
                              else:
                                  if (cmd == DONT):
                                      SendIAC(WONT, opt)
          return 3
      #   соединение с сервером
      #   ---------------------
      def connectSocket():
      
          global Interval
          global numInterval
      
      
          s = ''
          REC_TIME = 200
          SER.send('nrTest CONTEXT ... ')
          MDM.send("AT#SGACT?r",0)
          res = MDM.receive(REC_TIME)
          if (res.find('1,1') == -1):
              SER.send('ERROR!nrActivate CONTEXT ... ')
              MDM.send('AT#SGACT=1,1r', 10)
              delay = MOD.secCounter() + 20
              while(1):
                  if(delay < MOD.secCounter()):
                      SER.send('Not activated')
                      break
                  s = s + MDM.receive(10)
                  if(s.find('OK')!=-1):
                      break
          else:
              SER.send(' OK')
          MDM.send("AT#SGACT?r",0)
          res = MDM.receive(REC_TIME)
          if (res.find('1,1') != -1):
              SER.send('nr')
          s = ''
          SER.send('nrGPRS Connect ...')
          MDM.send('AT#SD=1,0,23,' + CONFIG.get('DEST_IP') + 'r', 10)
          MOD.sleep(5)
          while(1):
              s = s + MDM.receive(5)
              if(s.find('CONNECT')!=-1):
                  result = 0
                  break
              if(s.find('ERROR')!=-1):
                  result = -1
                  break
          if (result == 0):
              SER.send(' OKrn')
              SER.send('Try to Connect. Wait')
              SendIAC(WILL, TELOPT_TTYPE)
              SendIAC(DO, TELOPT_SGA)
              SendIAC(WONT, TELOPT_ECHO)
              SendIAC(DO, TELOPT_ECHO)
              Interval = MOD.secCounter() + DELAY_INT
              numInterval = 0
              return 1
          else:
              SER.send('ERROR. Connect failedrn')
              return 0
      #
      #   автологин на сервер
      #   -------------------
      #
      def AutoLogin (s):
          global autoLogin
          global isLogin
          global isPassword
      
          if (len(s) > 5 and autoLogin == 1):
      #   проверка на логин
              if (isLogin == 0):
                  if(s.find('login:') != -1):
                      MDM.send('' + CONFIG.get('GPRS_USER') + 'r', 5)
                      isLogin = 1
                      return 6
              else:
                  if (isPassword == 0):
                      if(s.find('Password:') != -1):
                          MDM.send('passwordr', 5)
                          isPassword = 1
                          return 9
          return 0
      
      #
      #   чтение конфигурации
      #   -------------------
      #
      def ReadConfig():
          return 0
      #
      #   соединение с сервером
      #   ---------------------
      #
      def Connect():
          global isConnect
          global exitScript
          global restartModem
      
          if (exitScript == 1):
              isConnect = 0
              return
          if (isConnect != 1):
              if (restartModem == 0):
                  SER.send('r                          r')
                  SER.send('Connect to server (Y/N)? ')
              while (1):
                  c = SER.readbyte()
      # клавиша 'y'
                  if (c != -1):
                      SER.sendbyte(c)
                      if (c == ord('y') or c == ord('Y')):
                          if (connectSocket() == 1):
                              isConnect = 1
                              break
                          else:
                              break
      # клавиша 'q'
                      else:
                          if (c == ord('!')):
                              exitScript = 1
                              break
      # клавиша 's'
                          else:
                              if (c == ord('\')):
                                  SER.send('rnSet Login [' + CONFIG.get('GPRS_USER') + '] :')
                                  s = ''
                                  while (1):
                                      c = SER.readbyte()
                                      if (c != -1):
                                          SER.sendbyte(c)
                                          if (c == ord('r')):
                                              break
                                          s = s + chr(c)
                                  if (len(s) > 0):
                                      CONFIG.set('GPRS_USER', s)
                                  SER.send('rnSet ID [' + CONFIG.get('SECONDARY_ID') + '] :')
                                  s = ''
                                  while (1):
                                      c = SER.readbyte()
                                      if (c != -1):
                                          SER.sendbyte(c)
                                          if (c == ord('r')):
                                              break
                                          s = s + chr(c)
                                  if (len(s) > 0):
                                      CONFIG.set('SECONDARY_ID', s.upper())
                                  else:
                                      SER.send('rn')
                                  SER.send('rnWrite Config ...')
                                  CONFIG.write()
                                  break
      # клавиша 'r'
                              else:
                                  if (c == ord('$')):
                                      SER.send(' rnReset modem ...             rn')
                                      restartModem = 1
                                      MOD.watchdogEnable(2)
                                      break
      # клавиша 'a'
                                  else:
                                      if (c == ord('|')):
                                          SER.send('rnIP: ' + IP_address + 'rn')
                                          break
                                      else:
                                          break
      #
      #   читаем COM-порт
      #   ---------------
      #
      def ReadCom():
          global isEscape
          global isConnect
          global echoTln
          global sgaFlg
          global TelqualSend
          global isLogin
          global isPassword
          global Interval
          global numInterval
      
          delay = 0L
      
          s = SER.read()
          if(len(s) > 0):
              if (s.find('x07') != -1):
                  MOD.watchdogEnable(2)
                  SER.send(' rnReset modem ...             rn')
              Interval = MOD.secCounter() + DELAY_INT
              numInterval = 0
              MDM.send(s, 2)
              if (s.find('%') != -1):
                  isEscape = isEscape + 1
                  if (isEscape == 4):
                      SER.send('x1b[Hx1b[JEscape found.                rn')
                      MOD.sleep(50)
                      while(1):
                          MOD.sleep(50)
                          MDM.send('+', 1)
                          MDM.send('+', 1)
                          MDM.send('+', 1)
                          MOD.sleep(50)
                          delay = MOD.secCounter() + 20
                          while(1):
                            if(delay < MOD.secCounter()):
                              SER.send('Escape timeout.  Reset module...        rn')
                              MOD.watchdogEnable(2)
                              break
                            s = s + MDM.receive(5)
                            if(s.find('OK')!=-1):
                              break
                          if(s.find('OK')!=-1):
                              break
                      SER.send('Socket shutdown.              rn')
                      MDM.send('AT#SH=1r', 10)
                      MOD.sleep(20)
                      delay = MOD.secCounter() + 20
                      while(1):
                        if(delay < MOD.secCounter()):
                          SER.send('Shutdown timeout.              rn')
                          break
                        s = s + MDM.receive(5)
                        if(s.find('OK')!=-1):
                          break
                      SER.send('Wait disconnect ... ')
                      while(1):
                        MDM.send('AT#SSr', 10)
                        MOD.sleep(40)
                        s = s + MDM.receive(10)
                        if(s.find('1,0')!=-1):
                          break
                      SER.send('OKrn')
                      SER.send('Connection closed.            rn')
                      isConnect = 0
                      echoTln = 1
                      sgaFlg = 1
                      TelqualSend = 0
                      isLogin = 0
                      isPassword = 0
                      isEscape = 0
              else:
                  isEscape = 0
          else:
              if(Interval < MOD.secCounter() and numInterval == 0):
                  MDM.send('%c' % chr(0x7), 5)
                  SER.send('%c' % chr(0x7))
                  numInterval = 1
      #
      #   проверка соединения
      #   -------------------
      #
      def TestConnection(s):
          global isConnect
          global echoTln
          global sgaFlg
          global TelqualSend
          global isLogin
          global isPassword
          global Interval
          global numInterval
      
          if(s.find('NO CARRIER') != -1):
              SER.send('rnrnrnrnConnection closedrn')
              isConnect = 0
              echoTln = 1
              sgaFlg = 1
              TelqualSend = 0
              isLogin = 0
              isPassword = 0
              return 0
          else:
              Interval = MOD.secCounter() + DELAY_INT
              numInterval = 0
              return 1
      #
      # ==================
      # основная программа
      # ==================
      #
      
      SER.set_speed(CONFIG.get('COM_SPEED'),'8N1')
      SER.send('rnStart Telnet Script. Ver. ' + version + ' (2019)rn')
      ReadConfig()
      if (checkNetwork() == 0):
          exitScript = 1
      IP_address = activateContext()
      while (1):
      #   проверка наличия соединения с сервером
          Connect()
      #   если есть соединение обрабатываем данные
          if (isConnect == 1):
              ReadCom()
      #       читаем telnet
              rcv = MDM.read()
              j = len(rcv)
              if (j > 0):
                  if (TestConnection(rcv) == 1):
                      AutoLogin(rcv)
                      if (rcv.find('xff') != -1):
                          res = ''
                          i = 0
                          while (i < j):
                              sym = ord(rcv[i])
                              if (sym == IAC):
                                  k = processIAC(rcv[i:j])
                                  if (k == IAC):
                                      SER.sendbyte(sym)
                                      i = i + 1
                                  else:
                                      if (k == 0):
                                          SER.sendbyte(sym)
                                          i = i + 1
                                      else:
                                          i = i + k
                              else:
                                  SER.sendbyte(sym)
                                  i = i + 1
                      else:
                          SER.send(rcv)
                          if (rcv.find('x05') != -1):
                              MDM.send('LXE/q/%d/APLUS/%sr' % (24, CONFIG.get('SECONDARY_ID')), 10)
      #   проверка на завершение работы скрипта
          else:
              if (exitScript == 1):
                  break
      # =========================
      # завершение работы скрипта
      # =========================
      SER.send('rnStop Scriptrn')
      MDM.send('AT#SGACT=1,0r', 10)
      

      Следует сразу отметить, что загружать текстовой вариант программы (с расширением .py) в модем практически бессмысленно. Время запуска текстового скрипта составляет несколько десятков минут (мне терпения дождаться так и нехватило). Поэтому следующий шаг — компиляция скрипта и получение файла с расширением .pyo.


      Описание изделия Bitcord CT-2-05.

      Модем «Bitcord CT-2-05» – это компактный модем для передачи данных, текстовых сообщений SMS и факсов в сетях сотовой связи поколений 2G/2.5G GSM, создан на основе модуля GL865-DUAL V3.1 Telit. Модем содержит широкий набор периферии – интерфейсы RS232, RS485 без гальванической изоляции, дискретные порты ввода GP-input 2шт, дискретные порты вывода GP-output 2шт. выхода типа открытый коллектор (ОК).

      Встроенный условный/безусловный сторожевой таймер обеспечивает как безусловную перезагрузку модема через настраиваемые интервалы времени кратный 1 часу.

      Модем может эксплуатироваться в качестве стандартного модема в одном из многочисленных режимов CSD, GPRS TCP/IP (при использовании совместно с ПК или внешним контроллером), SMS, так и исполнять пользовательские сценарии на языке Python, при помощи встроенного интерпретатора.

      Области применения.

        • Системы коммерческого учета электроэнергии
        • Системы M2M
        • Системы IoT
        • Удаленная диспетчеризация узлов учета тепла
        • Удаленная диспетчеризация электросчетчиков
        • Удаленная диспетчеризация узлов учета газа
        • Системы безопасности
        • Дистанционный контроль
        • Дистанционные измерения
        • Доступ в Интернет

      Характеристики изделия.

      Диапазоны частот и стандарты сотовой связи.

      • поддержка диапазонов EGSM900/DCS1800;
      • полное соответствие GSM/GPRS protocol stack 3GPP Release 4;
      • выходная мощность 2Вт (EGSM900) и 1Вт (DCS1800/PCS1900);
      • чувствительность: -108dBm (900МГц), -107dBm (1800МГц);

      Протоколы передачи данных и стандарты обработки данных, поддерживаемые модемом

      • CSD — V110;
      • AT-команды — 3GPP 27.005, 27.007 и пользовательские Telit AT команды;
      • Управление через удаленные AT-команды (Remote AT commands);
      • Serial port multiplexer 3GPP 27.010;
      • SIM Application Toolkit 3GPP TS 51.014;
      • Профили доступа к SIM;
      • Встроенный TCP/IP стек управляемый через AT-команды;
      • Поддержка технологии повышения чувствительности приема DARP/SAIC;
      • Поддержка технологии Enhanced Measured Report;
      • Поддержка протокола ITU-T V.24 serial link через один из интерфейсов RS232/RS485 с параметрами:
        • Диапазон скоростей передачи данных – от 300 до 115200 бит/сек;
        • Автоматическое определение скорости- до 115200 бит/сек
      • SMS:
        • Cell Broadcast;
        • Text and PDU mode;
        • Concatenated SMS;
        • SMS over GPRS;
        • Обмен сообщениями SMS в режиме point-point;
      • Дополнительные сервисы GSM:
        • Call forwarding;
        • Call barring;
        • Call waiting & call hold;
        • Advice of charge;
        • Calling line identification presentation (CLIP);
        • Calling line identification restriction (CLIR);
        • USSD;
        • Closed user group;
      • Дополнительные функции:
        • Телефонная книга SIM;
        • Фиксированные номера FDN;
        • Часы реального времени Real Time Clock;
        • Управление сигнализацией Alarm management;
        • Поддержка наборов символов IRA, GSM, 8859-1 и UCS2;
        • Определение «глушения»;
        • Встроенный TCP/IP стек, включая следующие протоколы TCP, IP, UDP, SMTP, ICMP и FTP;
        • EASY SCAN – автоматическое сканирование GSM частот, работает также без SIM карты;
      • Интерпретатор языка Python — доступно 0.8Мбайт энергонезависимой памяти и 1Мбайт ОЗУ для пользовательских скриптов;
      • FOTA – обновление прошивки модуля через AT-команды;

      Скорости передачи данных по радиоканалу.

      • Скорости передачи данных сотовая связь:
        • GPRS: Class 10 – DL 85.6 kbps/ UL 42.8 kBps;
      • CSD до 9,6 kbps, V110;

      Интерфейсы передачи данных и питания.

      • поддержка 2-х SIM карт;
      • держатель SIM-карты N1: лотковый с толкателем;
      • держатель SIM-карты N2: лотковый с толкателем;
      • поддержка типов SIM карт по питающему напряжению: 1.8В/3.0В;
      • разъем питания: 2-х контактный разрывной клеммник с шагом выводов 3.81мм;
      • интерфейс RS232:
        • полное соответствие стандарту RS232C (EIA)/V.24 (ITU-T);
        • поддержка сигналов RX/TX/CTS/RTS/DTR/RING/DSR/DTR;
        • разъем DRB-9FA, таблица выводов п.п. 8.3 РЭ;
        • скорость передачи данных до 115200 кбит/с;
      • интерфейс RS485:
        • полное соответствие стандарту ANSI-TIA/ (EIA-485-A:1998);
        • максимальная длина линии передачи – 1200;
        • максимальное количество устройств – 32;
        • скорость передачи данных до 115200 кбит/с;
        • RS-485 (GND, B-, A+) (3-х-контактный разрывной клеммник с шагом 3.81 мм);

      Внимание! Интерфейсы RS232 и RS485 подключены к одному и тому же порту UART модема, поэтому могут работать в режиме разделения времени.

        • GP-in (GP_in_1 = Вывод GPIO_5 GSM модуля, GP_in_2 = Вывод GPIO_6 GSM модуля, GND) – 2 входа типа TTL
            • тип входа — открытый катод диода, анод которого подключен к напряжению 2.8 В через поледовательный резистор 10 кОм;
            • защита входа от статического электричества — до 15 кВ;
            • максимально допустимое входное напряжение — 6 В (ограничено порогом срабатывания защиты от статического электричества);
            • высокий логический уровень — напряжение от 0.8 до 5.5 В;
            • низкий логический уровень — напряжение от 0 до 0.5 В;
            • входной ток:
        • от минус 200 мкА до 0 мкА при входном напряжении от 0 до 2.3 В
        • от 0 мкА до 50 мкА при входном напряжении от 2.3 до 5.5 В
            • GP-out (GP_out_1 = Вывод GPIO_2 GSM модуля, GP_out_2 = Вывод GPIO_3 GSM модуля, GND) – 2 выхода типа ОК (открытый коллектор)
              • тип выхода — открытый коллектор;
              • максимальное коммутируемое напряжение — 60В;
              • максимальный ток нагрузки — 500 мА;

          Примечание. При индуктивной нагрузке (например, при управлении обмоткой реле) необходим внешний защитный диод (параллельно нагрузке в обратном включении).

          Органы индикации.

          Устройство имеет 2 индикатора NET (GPIO_08), TIMER (GPIO_07), которые могут использованы в пользовательских скриптах, написанных на Python.

          • Индикатор NET (красный) – статус сети GSM;
          • Пиктограмма TIMER (желтый) — настройка и контроль таймера перезагрузки;

          Параметры источника питания.

          • диапазон входного напряжения питания: — +8В … + 36В;
          • защита от инверсии питающего постоянного напряжения на разъеме «Uвх» – есть;
          • потребляемый ток от источника питания 12В, в режиме передачи данных CSD или GPRS, не более — 300 мА;
          • кратковременный, потребляемый ток от источника питания в момент инициализации модема: не более 1000 мА;

          Условия эксплуатации и хранения.

          • рабочая температура: -40.. + 80°C;
          • температура хранения: -50.. + 85°C;
          • относительная влажность — от 5 до 95% RH;
          • максимальная влажность: 95% RH при +40°C;

          IP-класс защиты и конструктивное исполнение.

          • степень защиты по IEC 60529 (DIN 40050, ГОСТ 14254-96): IP40;

          Массо-габаритные характеристики.

          • Габариты: 62.6 x 39.6 x 69,5 мм (ШxВxД) без учета разъемов
          • Масса: 156гр. (153-159 гр.);

          Комплектность.

          • Устройство;
          • Упаковочная коробка;
          • Паспорт;
          • Антенна на магнитном основании с кабелем длиной 2.5-3.0 м. c разъемом SMA (наличие зависит от комплекта).
          • 3-х контактный 3.81мм для подключения интерфейса RS485 — 1 шт;
          • 3-х контактный 3.81мм для подключения GP_in, GP_out -2шт ;
          • 2-х контактный 3.81мм для подключения напряжения питания устройства;
          • Кабель интерфейсный DB9M-DB9F;

          Таблица модификаций.

          Название модели.

          Модем BITCORD CT-2-05./опции/

           

          Наименовании опции

          Описание опции

          BITCORD CT-2-05.

          RS232

          Интерфейс RS232

          BITCORD CT-2-05.

          RS485

          Интерфейс RS485 3-х контактный без

          гальванической изоляции

          BITCORD CT-2-05.

          gpi-x

          Цифровые входы общего

          назначения с указанием кол-ва через дефис

          BITCORD CT-2-05.

          gpook-x

          Цифровые выходы общего назначения каскад открытый коллектор с указанием кол-

          ва через дефис

          BITCORD CT-2-05.

          2ff-x

          Кол-во симкарт типоразмера

          2ff (miniSIM)

          BITCORD CT-2-05.

          ae

          металлический корпус

          BITCORD CT-2-05.

          ac

          Импульсный источник питания 90-240В

          переменного напряжения

          

          Radio Life — Работа с GPRS модемом Telit

          1. С чего начать.

          GPRS-модем Bitcord СТ2-05 представляет из себя небольшую коробочку с двумя слотами для сим-карт, индикаторами TIMER и NET и набором разъемов. На начальном этапе работы требуются три из них — разъем питания, RS232 и антенный разъем.

          Рис. 1. Внешний вид модема

          Задача, собственно, очень простая — обеспечить соединение модема с сервером TELNET и организовать прием/передачу данных через порт RS232 с помощью любой терминальной программы (Hyper Teminal, Putty и т. п.).

          Соединение будем устанавливать через Мегафон с фиксированным IP.

          Естественно, начать надо с того, чтобы вставить в слот для SIM1 сим-карту, присоединить антенну, затем подключить питание. Должен несколько раз мигнуть, затем загореться зеленый индикатор TIMER. В процессе работы он будет «подмигивать» с некоторым интервалом, который зависит от установленного времени таймера перезапуска модема.

          Частое мигание индикатора NET показывает, что соединения с оператором нет. Если он не переходит в режим «помаргивания» с интервалом примерно 3 сек., то надо проверить антенну, сим-карту и вообще наличие соответствующего оператора в данном месте.

          Конечно, требуется компьютер с нормальным 9-пиновым COM-портом. Если имеется только USB, то можно использовать преобразователь USB-COM.

          Порт RS232 модема по умолчанию установлен в режим автоматического определения скорости передачи, поэтому на компьютере можно установить скорость передачи любую. Я поставил 115200, 8 бит, 1 стоповый.

          Подключаем кабель модема к порту компьютера и приступаем к настройке.

          2. Информация о модели, сброс к «заводским» настройкам

          Сначала надо проверить «доступность» модема. Набираем команду AT:

          
          AT
          OK
          

          Если получили ответ OK, можно продолжать. Для начала определим версию модели и прошивки. Можно заметить, что команды модему можно набирать как прописными, так и строчными буквами:

          
          at+cgmm (модель)
          GL865-DUAL-V3.1
          OK
          at+cgmr (прошивка)
          16.01.173
          OK
          AT+GMI (информация о производителе)
          telit
          OK
          

          В процессе работы может потребоваться сбросить модем к начальным установкам. Обнаружить, что сбрасывает стандартная команда AT&F0(1) не удалось (похоже, что ничего). Поэтому приходится использовать следующие команды:

          
          at+cfun=4
          OK
          at+cmar=00000000
          OK
          

          Команда AT+CFUN=4 деактивирует SIM-карту и сбрасывает сетевое соединение. Команда AT+CMAR=00000000 сбрасывает данные пользователя. 00000000 — «секретный код». Возможно, на других модемах он может быть иным, но производитель данного устройства указал для сброса такой код.

          При этом форматируется память, поэтому все пользовательские скрипты в модеме также будут стерты. После сброса перестает работать индикатор NET, поэтому необходимо ввести следующие команды:

          
          at#sled=2,10,10
          OK
          at#sledsav
          OK
          at&w
          OK
          

          3. Настройка сетевых параметров

          Прежде всего — проверим установки и подключение SIM-карты:

          
          at#qss?    (запрос статуса SIM-карты)
          #QSS: 0,1  (0 — запрос статуса по команде QSS, 1 — SIM-карта вставлена)
          OK
          at+cpin?      (запрос готовности PIN-кода)
          +CPIN: READY  (ввод PIN-кода не требуется)
          OK
          at+cgreg?     (запрос статуса GPRS)
          +CGREG: 0,1   (0 — отключен код регистрации в сети, 1 — модем зарегистрирован в сети)
          OK
          at+cops?      (запрос имени оператора)
          +COPS: 0,0,"MegaFon RUS"
          OK
          

          Итак, SIM-карта подключилась к нужному оператору (в нашем случае — Мегафон) и теперь можно перейти к настройкам собственно соединения. Сначала проверим установки параметров сокетов:

          
          at#scfg?
          
          #SCFG: 1,1,300,90,600,50
          #SCFG: 2,1,300,90,600,50
          #SCFG: 3,1,300,90,600,50
          #SCFG: 4,2,300,90,600,50
          #SCFG: 5,2,300,90,600,50
          #SCFG: 6,2,300,90,600,50
          

          Команда выводит параметры для 6 возможных сокетов и двух (из 5 возможных) наборов контекстов PDP (Packet Data Protocol type). Нам требуется всего один сокет (для telnet-соединения), выберем для работы сокет 1 и контекст 1. Рассмотрим по порядку, что означают эти цифры для первого сокета.

          1 — номер сокета; 1 — номер контекста PDP, который используется для этого сокета; 300 — размер пакета данных, передаваемых по TCP-соединению; 90 — таймаут ожидания сброса соединения при отсутствии активности (в секундах); 600 — таймаут ожидания установки соединения (в сотнях миллисекунд); 50 — таймаут передачи данных (в сотнях миллисекунд), в случае, если пакет для передачи меньше размера пакета (в данном случае — 300 байт).

          Подробнее о значении этих параметров, возможно, скажу ниже, а пока можно оставить их «как есть».

          Теперь настроим параметры контекста соединения. Это делает команда CGDCONT:

          
          at+cgdcont=1,"IP","FixedIP.nw"
          

          Здесь: 1 — идентификатор контекста; IP — тип протокола передачи данных (интернет протокол); FixedIP.nw — имя точки доступа APN, зависит от оператора и им же должно сообщаться. В нашем случае используется фиксированный IP Мегафона. Команда CGDCONT имеет еще ряд параметров, но для нашего случая достаточно и этих (остальные отсавим по умолчанию), поэтому ими и ограничимся. Значения всех параметров контекста можно посмотреть той же командой:

          
          at+cgdcont?
          +CGDCONT: 1,"IP","FixedIP.nw","",0,0
          OK
          

          У нас настроен только один контекст, поэтому выводится только одна строка. При настройке нескольких контекстов PDP будет столько строк, сколько есть настроенных контекстов.

          Приведенные выше настройки сохраняются в памяти модема и при отключении питания, поэтому их достаточно ввести только один раз.

          4. Установка соединения

          Перед установкой соединения с сервером telnet еще раз проверим наличие соединения с сетевым оператором командой CREG:

          
          at+creg?
          +CREG: 0,1
          OK
          

          Результат аналогичен описанной выше команде CGREG.

          Далее активируем собственно контекст командой SGACT:

           
          at#sgact=1,1
          #SGACT: 100.000.000.000
          OK
          

          В строке команды можно дополнительно указать пользователя и пароль, но в нашем случае этого не требуется. При успешном завершении команда выводит IP адрес модема, который в случае SIM-карты со статическим адресом будет всегда один и тот же.

          И, наконец, устанавливаем telnet соединение с сервером с помощью команды SD:

          
          at#sd=1,0,23,200.200.100.100
          CONNECT
          œœ œ#œ'
          

          1 — номер сокета; 0 — протокол передачи TCP; 23 — номер порта; 200.200.100.100 — адрес сервера.

          Возможно, в большинстве случаев приведенного выше набора команд для работы будет достаточно, однако в нашем случае сервер не только ответил загадочным набором символов, но и категорически отказывается воспринимать ввод с клавиатуры. Можно добавить, что в случае бездействия, через интервал, установленный командой SCFG (при указанных выше настройках этот таймаут равен 90 сек) соединение будет сброшено, и модем выведет сообщение NO CARRIER. В любом случае желательно решить две проблемы:

          1. Набирать указанные команды при каждом подключении не очень удобно — всегда есть вероятность ошибки.

          2. Для установки соединения с нашим сервером необходимо сделать еще что-то, чтобы обеспечить возможность обмениваться данными.

          Для решения этих задач придется обратиться к скриптам, которые будем писать на языке Python, интерпретатор которого встроен в данный модем.

          5. Подготовка к работе со скриптами

          Для разработки скриптов необходим следующий минимум программного обеспечения:

          1. Любой текстовой редактор. Я пользуюсь tsWebEditor.

          2. Терминальный клиент HiperTerminal. Выбор этой программы обусловлен тем, что с ее помощью можно загружать в модем текстовые неоткомпилированные скрипты. Настройки программы для загрузки приведены в инструкции telit_easy_script_python_r18.pdf и будут приведены ниже.

          3. Для сборки скриптов нужен Telit Python package (TelitPy1.5.2+_v4.1.exe), который содержит необходимые библиотеки.

          4. Для загрузки в модем откомпилированных скриптов желателен Rsterm.

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

          6. Простой скрипт

          Перейдем к практике. Сначала напишем самый простой скрипт, который будет автоматически запускаться при включении питания модема (или по команде с терминала).

          Для общения с внешним миром в модемы Telit встроен ряд программных модулей, которые и будем использовать. Эти модули следующие:

          MOD — некоторый набор функций, имеющих отношение к работе самого модема.

          MDM — интерфейс, который отвечает за обмен командами и данными с внешним миром.

          SER — интерфейс последовательного порта, через который происходит прием/передача данных.

          Пока достаточно этих модулей, что будет видно из текста программы. Для того, чтобы использовать эти модули, их необходимо «подключить», для чего в текст программы надо вставить следующие строки:

          
          import MOD
          import MDM
          import SER
          

          После подключения необходимых модулей, пишем собственно скрипт:

          
          SER.send('GPRS CONTEXT activated ... rn')
          MDM.send('AT#SGACT=1,1r', 10)
          MOD.sleep(20)
          SER.send('GPRS Connect ...rn')
          MOD.sleep(20)
          MDM.send('AT#SD=1,0,23,200.200.100.100r', 10)
          

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

          MOD.sleep(20): простая задержка, выражающаяся в сотнях миллисекунд. Нужна для того, чтобы дать как модему, так и оператору время на выполнение операции.

          SER.send(‘GPRS CONTEXT activated … rn’): команда вывода на COM-порт информационного сообщения, чтобы мы могли хотя бы знать, что скрипт запустился.

          MDM.send(‘AT#SGACT=1,1r’, 10): описанная выше команда активации контекста PDP.

          MDM.send(‘AT#SD=1,0,23,200.200.100.100r’, 10): установка соединения с сервером.

          Полный текст скрипта получается такой:

          
          import MOD
          import MDM
          import SER
          
          SER.send('GPRS CONTEXT activated ... rn')
          MDM.send('AT#SGACT=1,1r', 10)
          MOD.sleep(20)
          SER.send('GPRS Connect ...rn')
          MOD.sleep(20)
          MDM.send('AT#SD=1,0,23,200.200.100.100r', 10)
          

          Сохраняем его в виде текстового файла с расширением«.py», например, test.py, и перейдем к процессу загрузки и запуска.

          Рис. 2. Настройки параметров COM-порта

          Прежде всего, приведем настройки HyperTerminal’а в соответствие с рекомендациями Telit. Рекомендованные настройки показаны на рис. 2—4.

          Рис. 3. Свойства терминала

          Рис. 4. Настройки параметров ASCII

          Передача данных в модем производится в режиме отправки текстового файла (см. рис. 5). Для этого вводится следующая команда:

          
          at#wscript="test.py",227
          
          >>>
          

          Где test.py — имя файла, а 227 — его размер. Естественно, у вас может быть и другой размер, в зависимости от оформления текста скрипта.

          Рис. 5. Загрузка файла в модем.

          После появления на экране терминала приглашения к передаче (>>>), следует успеть перейти к передаче файла. Обратите внимание, что для того, чтобы иметь возможность указать терминалу имя передаваемого файла в окошке Тип файлов следует указать Все файлы (*.*).

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

          
          at#lscript
          #LSCRIPT: "test.py",227
          
          OK
          

          Далее настроим режим запуска скрипта. Это можно сделать с помощью команды STARTMODESCR, применив ее следующим образом:

          
          at#startmodescr=1,30
          
          OK
          

          Первый параметр определяет режим запуска скрипта: 0 — запуск зависит от уровня сигнала DTR. На мой взгляд, это не совсем удобно. 1 — запуск скрипта произойдет автоматически через интервал, указанный во втором параметре команды (в нашем случае — 30 сек.), при условии, что в течение этого интервала не будет введена команда через порт RS232. Мне больше нравится этот вариант, т. к. он позволяет надежно остановить запуск скрипта, что вполне может понадобится при отладке.

          Теперь надо указать модему, какой скрипт следует запустить (ведь скриптов в модем можно записать не один — памяти для этого достаточно). Это устанавливается следующей командой:

          
          at#escript="test.py"
          
          OK
          at#escript?
          #escript="test.py"
          
          OK
          

          В заключение добавлю, что если по каким-то причинам вы остановили запуск скрипта вводом команды через порт RS232, то его при необходимости можно запустить «вручную»:

          
          at#execscr
          
          OK
          

          При этом будет запущен скрипт, ранее указанный командой ESCRIPT.

          Теперь можно перезапустить модем (например, выключив и включив питание) и посмотреть что у нас получилось. В общем, при нормальных условиях, на экране терминала, подключенного к модему, должно появиться примерно следующее: FF FD 18 FF FD 20 FF FD 23 FF FD 27 (я перевел «непечатные» символы в 16-ричный код, чтобы дальнейшее было понятнее).

          Что с этим делать, тем более, что, повторю, никакого отклика на нажатие клавиш на терминале не происходит?

          7. Работа с командами IAC

          Дело в том, что в протоколе telnet предусмотрен запрос сервером у клиента параметров соединения или самого клиента. Обмен производится с помощью команд, предваряемых кодом IAC (Interpret As Command — интерпретировать как команду, код 0xFF).

          Полный список команд довольно длинный, однако в нашем случае достаточно обойтись следующим набором:

          
          IAC =  0xFF
          
          DONT = 0xFE
          DO =   0xFD
          WONT = 0xFC
          WILL = 0xFB
          SB =   0xFA
          AYT =  0xF6
          SE =   0xF0
          
          TELOPT_ECHO =        0x01
          TELOPT_SGA =         0x03
          TELOPT_STATUS =      0x05
          TELOPT_SUPDUP =      0x15
          TELOPT_TTYPE =       0x18
          TELOPT_NAWS =        0x1F
          TELOPT_TSPEED =      0x20
          TELOPT_LFLOW =       0x21
          TELOPT_XDISPLOC =    0x23
          TELOPT_NEW_ENVIRON = 0x27
          
          TELQUAL_IS =   0x00
          TELQUAL_SEND = 0x01
          

          Теперь можно «расшифровать» запрос сервера: WILL TTYPE — запрос типа терминала, WILL TSPEED — запрос скорости терминала, WILL XDISPLOC — что-то вроде запроса местоположения и, наконец, WILL NEW_ENVIRON — запрос переменных среды.

          В большинстве случаев достаточно указать в ответ тип терминала:

          
          #
          #   посылка на сервер команды IAC
          #   -----------------------------
          def SendIAC(cmd, opt):
              s = ('%c%c%c' % (chr(IAC), chr(cmd), chr(opt)))
              MDM.send('%s' % s, 5)
          #
          #   обработка запросов сервера IAC
          #   ------------------------------
          def processIAC (s):
              global echoTln
              global sgaFlg
              global TelqualSend
          
              if (s[1] == chr(IAC)):
                  return IAC
              else:
                  cmd = ord(s[1])
                  opt = ord(s[2])
                  SER.sendbyte(ord('.'))
          #       обработка TELOPT_ECHO
                  if (opt == TELOPT_ECHO):
                      if (cmd == WILL):
                          if (echoTln == 1):
                              echoTln = 0
                              SendIAC(DO, TELOPT_ECHO)
                      else:
                          if (cmd == WONT):
                              if(echoTln == 0):
                                  echoTln = 1
                                  SendIAC(DONT, TELOPT_ECHO)
                          else:
                              if (cmd == DO):
                                  echoTln = 0
                                  so = ('%c%c%c%c%c%c' % (chr(IAC),chr(WONT),chr(TELOPT_ECHO),chr(IAC),chr(DO),chr(TELOPT_ECHO)))
                                  MDM.send('%s' % so, 5)
                              else:
                                  if (cmd == DONT):
                                      echoTln = 0
                                      SendIAC(WONT, TELOPT_ECHO)
          #       обработка TELOPT_SGA
                  else:
                      if (opt == TELOPT_SGA):
                          if (cmd == WONT):
                              sgaFlg = 1
                              if (echoTln == 0):
                                  echoTln = 1
                                  SendIAC(DONT, TELOPT_SGA)
                          else:
                              if (cmd == WILL):
                                  sgaFlg = 0
                                  if (echoTln != 0):
                                      so = ('%c%c%c%c%c%c' % (chr(IAC),chr(DO),chr(TELOPT_SGA),chr(IAC),chr(DO),chr(TELOPT_ECHO)))
                                      MDM.send('%s' % so, 5)
          #           обработка TELOPT_TTYPE
                      else:
                          if (opt == TELOPT_TTYPE):
                              if (cmd == DO):
                                  SendIAC(WILL, TELOPT_TTYPE)
                              else:
                                  if (cmd == SB):
                                      n = 0
                                      flag = 0
                                      while (n < 41):
                                          y = s[n+3]
                                          if (y == chr(IAC)):
                                              flag = 1
                                              n = n + 1
                                          else:
                                              if (flag != 0 and y == chr(SE)):
                                                  break
                                              else:
                                                  flag = 0
                                                  n = n + 1
                                      if (flag == 0):
                                          return n + 3
                                      if (s[3] == chr(TELQUAL_SEND)):
                                          so = ('%c%c%c%c%s%c%c' % (chr(IAC),chr(SB),chr(TELOPT_TTYPE),chr(TELQUAL_IS),TermType,chr(IAC),chr(SE)))
                                          MDM.send('%s' % so, 5)
                                          return n+3
          #               обработка других опций
                          else:
                              if (cmd == WILL):
                                  SendIAC(DONT, opt)
                              else:
                                  if (cmd == DO):
                                      so = ('%c%c%c%c%c%c' % (chr(IAC),chr(WONT),chr(opt),chr(IAC),chr(DONT),chr(opt)))
                                      MDM.send('%s' % so, 5)
                                  else:
                                      if (cmd == DONT):
                                          SendIAC(WONT, opt)
              return 3
          

          Это, конечно, далеко не полный обработчик запросов IAC, но для начала работы его вполне достаточно.

          Осталось добавить конфигурационный файл (назвав его, например, settings.ini) в котором можно хранить настройки программы, например, такого вида:

          
          AUTH_TYPE::1::
          APN::FixedIP.nw::
          GPRS_USER::user::
          GPRS_PASSWD::password::
          DEST_IP::200.200.100.100::
          SECONDARY_ID::1234::
          COM_SPEED::57600::
          

          Можно обратить внимание на строчку SECONDARY_ID. Дело в том, что в некоторых случаях сервер может отправлять клиенту запрос для получения дополнительных параметров соединения. Код запроса — 0x05. При ответе на этот запрос клиент может отправить, кроме всего прочего, и собственный идентификатор. Это бывает полезно, если необходимо, например, идентифицировать клиента не только по IP или логину.

          Для работы с файлом конфигурации создадим дополнительный класс Config, записав его в отдельный файл config.py.

          
          class Config:
              def __init__(self):
                  self.config = {}
          
              def get(self, k):
                  return self.config[k]
          
              def set(self, k, v):
                  self.config[k] = v
          
              def read(self):
                  try:
                      fh = open("settings.ini", "r")
                      try:
                          lines = fh.readlines()
                          for l in lines:
                              kv = l.strip().split('::')
                              self.config[kv[0]] = kv[1]
                      finally:
                          fh.close()
                  except IOError:
                      print "Configuration file not found."
          
              def write(self):
                  try:
                      fh = open("settings.ini", "w")
                      try:
                          lines = []
                          for k in self.config.keys():
                              lines.append(k + "::" + self.config[k] + "::rn")
                          fh.writelines(lines)
                      finally:
                          fh.close()
                  except IOError:
                      print "Configuration file not found."
          
              def dump(self):
                  for k in self.config.keys():
                      print k + "::" + self.config[k]
          

          И, наконец, полный текст программы:

          
          import MOD
          import MDM
          import SER
          import MDM2
          import config
          CONFIG = config.Config()
          CONFIG.read()
          version = '04.151'
          IAC = 255
          DONT = 254
          DO = 253
          WONT = 252
          WILL = 251
          SB = 250
          AYT = 246
          SE = 240
          TELOPT_ECHO = 1
          TELOPT_SGA = 3
          TELOPT_STATUS = 5
          TELOPT_SUPDUP = 21
          TELOPT_TTYPE = 24
          TELOPT_NAWS = 31
          TELOPT_TSPEED = 32
          TELOPT_LFLOW = 33
          TELOPT_XDISPLOC = 35
          TELOPT_NEW_ENVIRON = 39
          TELQUAL_IS = 0
          TELQUAL_SEND = 1
          echoTln = 1
          sgaFlg = 1
          exitScript = 0
          restartModem = 0
          secondaryID = 0
          isConnect = 0
          TelqualSend = 0
          TermType = 'VT220'
          TermSpeed = '38400,38400'
          autoLogin = 1
          isLogin = 0
          isPassword = 0
          isEscape = 0
          Interval = 0L
          numInterval = 0
          DELAY_INT = 850
          IP_address = ''
          #
          #   проверка наличия сети
          #   ---------------------
          def checkNetwork():
          SER.send('Try to Find Net ...')
          MOD.sleep(20)
          REC_TIME = 200
          for _ in range(10):
          MDM.send("AT+CREG?r",0)
          res = MDM.receive(REC_TIME)
          if (res.find('0,1')!=-1):
          SER.send(' OKnr')
          return 1
          else:
          MOD.sleep(50)
          SER.send(' Net Not Foundnr')
          return 0
          #
          #   активация контекста (вхождение в сеть)
          #   --------------------------------------
          def activateContext():
          REC_TIME = 200
          s = ''
          SER.send('GPRS CONTEXT setup ... ')
          MDM.send('AT+CGDCONT=1,"IP","' + CONFIG.get('APN') + '"r', 0)
          while(1):
          s = s + MDM.receive(10)
          if(s.find('OK')!=-1):
          SER.send(' OKnr')
          break
          if(s.find('ERROR')!=-1):
          SER.send(' ERRORnr')
          break
          SER.send('GPRS CONTEXT activated ...')
          MDM.send('AT#SGACT=1,1r', 10)
          MOD.sleep(20)
          s = ''
          while(1):
          s = s + MDM.receive(10)
          if(s.find('OK')!=-1):
          result = 0
          break
          if(s.find('ERROR')!=-1):
          result = -1
          break
          if (result == 0):
          str_IP = s.split(chr(13))
          i_str_IP = str_IP[1].find(': ')
          My_IP = str_IP[1][i_str_IP + 2:]
          SER.send(' OKnr')
          return My_IP
          else:
          SER.send('ERROR.rn')
          return 0
          #
          #   посылка на сервер команды IAC
          #   -----------------------------
          def SendIAC(cmd, opt):
          s = ('%c%c%c' % (chr(IAC), chr(cmd), chr(opt)))
          MDM.send('%s' % s, 5)
          #
          #   обработка запросов сервера IAC
          #   ------------------------------
          def processIAC (s):
          global echoTln
          global sgaFlg
          global TelqualSend
          if (s[1] == chr(IAC)):
          return IAC
          else:
          cmd = ord(s[1])
          opt = ord(s[2])
          SER.sendbyte(ord('.'))
          #       обработка TELOPT_ECHO
          if (opt == TELOPT_ECHO):
          if (cmd == WILL):
          if (echoTln == 1):
          echoTln = 0
          SendIAC(DO, TELOPT_ECHO)
          else:
          if (cmd == WONT):
          if(echoTln == 0):
          echoTln = 1
          SendIAC(DONT, TELOPT_ECHO)
          else:
          if (cmd == DO):
          echoTln = 0
          so = ('%c%c%c%c%c%c' % (chr(IAC),chr(WONT),chr(TELOPT_ECHO),chr(IAC),chr(DO),chr(TELOPT_ECHO)))
          MDM.send('%s' % so, 5)
          else:
          if (cmd == DONT):
          echoTln = 0
          SendIAC(WONT, TELOPT_ECHO)
          #       обработка TELOPT_SGA
          else:
          if (opt == TELOPT_SGA):
          if (cmd == WONT):
          sgaFlg = 1
          if (echoTln == 0):
          echoTln = 1
          SendIAC(DONT, TELOPT_SGA)
          else:
          if (cmd == WILL):
          sgaFlg = 0
          if (echoTln != 0):
          so = ('%c%c%c%c%c%c' % (chr(IAC),chr(DO),chr(TELOPT_SGA),chr(IAC),chr(DO),chr(TELOPT_ECHO)))
          MDM.send('%s' % so, 5)
          #           обработка TELOPT_TTYPE
          else:
          if (opt == TELOPT_TTYPE):
          if (cmd == DO):
          SendIAC(WILL, TELOPT_TTYPE)
          else:
          if (cmd == SB):
          n = 0
          flag = 0
          while (n < 41):
          y = s[n+3]
          if (y == chr(IAC)):
          flag = 1
          n = n + 1
          else:
          if (flag != 0 and y == chr(SE)):
          break
          else:
          flag = 0
          n = n + 1
          if (flag == 0):
          return n + 3
          if (s[3] == chr(TELQUAL_SEND)):
          so = ('%c%c%c%c%s%c%c' % (chr(IAC),chr(SB),chr(TELOPT_TTYPE),chr(TELQUAL_IS),TermType,chr(IAC),chr(SE)))
          MDM.send('%s' % so, 5)
          return n+3
          #               обработка других опций
          else:
          if (cmd == WILL):
          SendIAC(DONT, opt)
          else:
          if (cmd == DO):
          so = ('%c%c%c%c%c%c' % (chr(IAC),chr(WONT),chr(opt),chr(IAC),chr(DONT),chr(opt)))
          MDM.send('%s' % so, 5)
          else:
          if (cmd == DONT):
          SendIAC(WONT, opt)
          return 3
          #   соединение с сервером
          #   ---------------------
          def connectSocket():
          global Interval
          global numInterval
          s = ''
          REC_TIME = 200
          SER.send('nrTest CONTEXT ... ')
          MDM.send("AT#SGACT?r",0)
          res = MDM.receive(REC_TIME)
          if (res.find('1,1') == -1):
          SER.send('ERROR!nrActivate CONTEXT ... ')
          MDM.send('AT#SGACT=1,1r', 10)
          delay = MOD.secCounter() + 20
          while(1):
          if(delay < MOD.secCounter()):
          SER.send('Not activated')
          break
          s = s + MDM.receive(10)
          if(s.find('OK')!=-1):
          break
          else:
          SER.send(' OK')
          MDM.send("AT#SGACT?r",0)
          res = MDM.receive(REC_TIME)
          if (res.find('1,1') != -1):
          SER.send('nr')
          s = ''
          SER.send('nrGPRS Connect ...')
          MDM.send('AT#SD=1,0,23,' + CONFIG.get('DEST_IP') + 'r', 10)
          MOD.sleep(5)
          while(1):
          s = s + MDM.receive(5)
          if(s.find('CONNECT')!=-1):
          result = 0
          break
          if(s.find('ERROR')!=-1):
          result = -1
          break
          if (result == 0):
          SER.send(' OKrn')
          SER.send('Try to Connect. Wait')
          SendIAC(WILL, TELOPT_TTYPE)
          SendIAC(DO, TELOPT_SGA)
          SendIAC(WONT, TELOPT_ECHO)
          SendIAC(DO, TELOPT_ECHO)
          Interval = MOD.secCounter() + DELAY_INT
          numInterval = 0
          return 1
          else:
          SER.send('ERROR. Connect failedrn')
          return 0
          #
          #   автологин на сервер
          #   -------------------
          #
          def AutoLogin (s):
          global autoLogin
          global isLogin
          global isPassword
          if (len(s) > 5 and autoLogin == 1):
          #   проверка на логин
          if (isLogin == 0):
          if(s.find('login:') != -1):
          MDM.send('' + CONFIG.get('GPRS_USER') + 'r', 5)
          isLogin = 1
          return 6
          else:
          if (isPassword == 0):
          if(s.find('Password:') != -1):
          MDM.send('passwordr', 5)
          isPassword = 1
          return 9
          return 0
          #
          #   чтение конфигурации
          #   -------------------
          #
          def ReadConfig():
          return 0
          #
          #   соединение с сервером
          #   ---------------------
          #
          def Connect():
          global isConnect
          global exitScript
          global restartModem
          if (exitScript == 1):
          isConnect = 0
          return
          if (isConnect != 1):
          if (restartModem == 0):
          SER.send('r                          r')
          SER.send('Connect to server (Y/N)? ')
          while (1):
          c = SER.readbyte()
          # клавиша 'y'
          if (c != -1):
          SER.sendbyte(c)
          if (c == ord('y') or c == ord('Y')):
          if (connectSocket() == 1):
          isConnect = 1
          break
          else:
          break
          # клавиша 'q'
          else:
          if (c == ord('!')):
          exitScript = 1
          break
          # клавиша 's'
          else:
          if (c == ord('')):
          SER.send('rnSet Login [' + CONFIG.get('GPRS_USER') + '] :')
          s = ''
          while (1):
          c = SER.readbyte()
          if (c != -1):
          SER.sendbyte(c)
          if (c == ord('r')):
          break
          s = s + chr(c)
          if (len(s) > 0):
          CONFIG.set('GPRS_USER', s)
          SER.send('rnSet ID [' + CONFIG.get('SECONDARY_ID') + '] :')
          s = ''
          while (1):
          c = SER.readbyte()
          if (c != -1):
          SER.sendbyte(c)
          if (c == ord('r')):
          break
          s = s + chr(c)
          if (len(s) > 0):
          CONFIG.set('SECONDARY_ID', s.upper())
          else:
          SER.send('rn')
          SER.send('rnWrite Config ...')
          CONFIG.write()
          break
          # клавиша 'r'
          else:
          if (c == ord('$')):
          SER.send(' rnReset modem ...             rn')
          restartModem = 1
          MOD.watchdogEnable(2)
          break
          # клавиша 'a'
          else:
          if (c == ord('|')):
          SER.send('rnIP: ' + IP_address + 'rn')
          break
          else:
          break
          #
          #   читаем COM-порт
          #   ---------------
          #
          def ReadCom():
          global isEscape
          global isConnect
          global echoTln
          global sgaFlg
          global TelqualSend
          global isLogin
          global isPassword
          global Interval
          global numInterval
          delay = 0L
          s = SER.read()
          if(len(s) > 0):
          if (s.find('x07') != -1):
          MOD.watchdogEnable(2)
          SER.send(' rnReset modem ...             rn')
          Interval = MOD.secCounter() + DELAY_INT
          numInterval = 0
          MDM.send(s, 2)
          if (s.find('%') != -1):
          isEscape = isEscape + 1
          if (isEscape == 4):
          SER.send('x1b[Hx1b[JEscape found.                rn')
          MOD.sleep(50)
          while(1):
          MOD.sleep(50)
          MDM.send('+', 1)
          MDM.send('+', 1)
          MDM.send('+', 1)
          MOD.sleep(50)
          delay = MOD.secCounter() + 20
          while(1):
          if(delay < MOD.secCounter()):
          SER.send('Escape timeout.  Reset module...        rn')
          MOD.watchdogEnable(2)
          break
          s = s + MDM.receive(5)
          if(s.find('OK')!=-1):
          break
          if(s.find('OK')!=-1):
          break
          SER.send('Socket shutdown.              rn')
          MDM.send('AT#SH=1r', 10)
          MOD.sleep(20)
          delay = MOD.secCounter() + 20
          while(1):
          if(delay < MOD.secCounter()):
          SER.send('Shutdown timeout.              rn')
          break
          s = s + MDM.receive(5)
          if(s.find('OK')!=-1):
          break
          SER.send('Wait disconnect ... ')
          while(1):
          MDM.send('AT#SSr', 10)
          MOD.sleep(40)
          s = s + MDM.receive(10)
          if(s.find('1,0')!=-1):
          break
          SER.send('OKrn')
          SER.send('Connection closed.            rn')
          isConnect = 0
          echoTln = 1
          sgaFlg = 1
          TelqualSend = 0
          isLogin = 0
          isPassword = 0
          isEscape = 0
          else:
          isEscape = 0
          else:
          if(Interval < MOD.secCounter() and numInterval == 0):
          MDM.send('%c' % chr(0x7), 5)
          SER.send('%c' % chr(0x7))
          numInterval = 1
          #
          #   проверка соединения
          #   -------------------
          #
          def TestConnection(s):
          global isConnect
          global echoTln
          global sgaFlg
          global TelqualSend
          global isLogin
          global isPassword
          global Interval
          global numInterval
          if(s.find('NO CARRIER') != -1):
          SER.send('rnrnrnrnConnection closedrn')
          isConnect = 0
          echoTln = 1
          sgaFlg = 1
          TelqualSend = 0
          isLogin = 0
          isPassword = 0
          return 0
          else:
          Interval = MOD.secCounter() + DELAY_INT
          numInterval = 0
          return 1
          #
          # ==================
          # основная программа
          # ==================
          #
          SER.set_speed(CONFIG.get('COM_SPEED'),'8N1')
          SER.send('rnStart Telnet Script. Ver. ' + version + ' (2019)rn')
          ReadConfig()
          if (checkNetwork() == 0):
          exitScript = 1
          IP_address = activateContext()
          while (1):
          #   проверка наличия соединения с сервером
          Connect()
          #   если есть соединение обрабатываем данные
          if (isConnect == 1):
          ReadCom()
          #       читаем telnet
          rcv = MDM.read()
          j = len(rcv)
          if (j > 0):
          if (TestConnection(rcv) == 1):
          AutoLogin(rcv)
          if (rcv.find('xff') != -1):
          res = ''
          i = 0
          while (i < j):
          sym = ord(rcv[i])
          if (sym == IAC):
          k = processIAC(rcv[i:j])
          if (k == IAC):
          SER.sendbyte(sym)
          i = i + 1
          else:
          if (k == 0):
          SER.sendbyte(sym)
          i = i + 1
          else:
          i = i + k
          else:
          SER.sendbyte(sym)
          i = i + 1
          else:
          SER.send(rcv)
          if (rcv.find('x05') != -1):
          MDM.send('LXE/q/%d/APLUS/%sr' % (24, CONFIG.get('SECONDARY_ID')), 10)
          #   проверка на завершение работы скрипта
          else:
          if (exitScript == 1):
          break
          # =========================
          # завершение работы скрипта
          # =========================
          SER.send('rnStop Scriptrn')
          MDM.send('AT#SGACT=1,0r', 10)
          

          Следует сразу отметить, что загружать текстовой вариант программы (с расширением .py) в модем практически бессмысленно. Время запуска текстового скрипта составляет несколько десятков минут (мне терпения дождаться так и нехватило). Поэтому следующий шаг — компиляция скрипта и получение файла с расширением .pyo.


          Понравилась статья? Поделить с друзьями:
        • Bissell vac and steam инструкция
        • Bissell proheat all surface big green инструкция на русском языке
        • Bissell fibre cleansing инструкция по применению
        • Bissell fiber cleansing инструкция по применению
        • Bissell 1670f инструкция на русском