Over The Air Provision (OTAP) как функция модема TC65Т
Это мое повествование об одном из свойств уникального GSM модема SIEMENS TC65
http://tc65-terminal.ru или http://www.cinterion.com/tc65t.html .
Речь вовсе не о том, что этот модем поддерживает JAVA программирование, что при наличии в нем десяти дискретных входов-выходов и двух аналоговых входов делает его незаменимым при разработке систем удаленного контроля технологических процессов по GSM/GPRS. Речь о том, что делать, если потребовалось обновить уже работающую на модеме программу, а сам модем находиться в несколько удаленном месте.
И так воспользуемся тем, что на английском звучит так:
Secure data transfer and easy software updates over the air (OTA)
т. е. обеспечивает передачу данных и удобное обновление программы через эфир. Будем называть эту возможность OTAP функцией.
Разбираемся дальше и в документации находим рисунок:
и еще определенные требования к СМС: An OTAP control SM must use a Submit PDU with Class1, PID $7d and 8 bit encoding.
Install operation:
JADURL:http://www.greatcompany.com/coolapps/mega.jad
Second SM:
OTAP_IMPNG
PWD:secret
BEARER:gprs
APNORNUM:access.to-thenet.net
NETUSER:nobody
NETPWD:nothing
DNS:192.168.1.2
START:install
Delete operation:
OTAP_IMPNG
PWD:secret
APPDIR:a:/work/appdir
START:delete
Понятно, что мы должны на каком-то web-сервере выложить нашу обновленную программу в виде файлов *.jad и *.jar. Для этого даже можно воспользоваться www.narod.ru. Далее послать СМС специального вида на модем, и модем самостоятельно через GPRS скачает и запустит новую программу. Смущают только некие требования к СМС в виде: PDU with Class 1 и Pid $7d and 8 bit encoding. Почему-то приходит мысль, что обычный мобильный телефон для этой операции не подойдет и нужен еще один GSM модем, а формировать СМС придется с помощью специальной программы, которую сейчас разработаем. Так что же из себя представляет СМС в виде: PDU with Class 1 и Pid $7d and 8 bit encoding. Пытаемся найти ответ на сайте SMS and PDU format . Там весьма подробно разобрано СМС в виде: PDU with Class 0 и Pid $00 and 7 bit encoding c текстом hellohello. На модем-передатчик в этом случае необходимо подать следующие команды:
AT+CMGF=0 //Set PDU mode AT+CSMS=0 //Check if modem supports SMS commands AT+CMGS=23 //Send message, 23 octets (excluding the two initial zeros) >0011000B916407281553F80000AA0AE8329BFD4697D9EC37<ctrl-z>
Теперь рассмотрим, что же нужно нам.
Команду: AT+CMGF=0 используем без изменений т. к. модем надо таки переключить в режим PDU.
Команда: AT+CMGS= XX говорит о размере СМС в байтах, т. е. нам придется составить СМС, определить его размер, подать эту команду с цифрами обозначающими размер нашей СМС. Причем размер СМС не может превышать 140 байт (XX максимально может быть 139 т. к. считаем с 0), в противном случае полезный текст отправляется несколькими СМС не превышающими 140 байт.
Ну, а далее на модем-передатчик через COM-порт передадим, составленное ранее СМС:
0011000B91 - используется без изменения,
6407281553F8 – номер телефона (46708251358) куда будет отправлено СМС,
00 - TP-PID в нашем случае должно быть 7d,
00 - TP-DCS, здесь Class 0 и 7 bit encoding. В нашем случае должно быть 15 т. е. Class 1 и
8 bit encoding,
AA – оператор связи будет пытаться доставить СМС адресату в течении 4-х дней,
0А – размер в байтах собственно полезного текста - нам тоже придется считать,
E8329BFD4697D9EC37 – это закодировано hellohello с использованием 7 битового кодирования, нам же надо 8 битовое кодирование и вовсе не hellohello, а что-то на подобие:
OTAP_IMPNG
PWD:
APNORNUM:internet.tele2.ru
JADURL:http://www.csu-tst.narod.ru/MyTC65.jar
APPDIR:a:\
BEARER:gprs
START:install
И так: запускаем С++ Builder 6, создаем новый проект. На форму кидаем: во-первых два поля Edit для ввода номера телефона и идентификатора , два элемента ListBox где будут отображаться номера телефонов уже существующих в базе данных и две кнопки “Записать” и “Удалить”; далее располагаем поля Edit, предназначенные для ввода содержимого нашей СМС и кнопка “Make SMS” с помощью которой СМС сгенерируется и сохраниться в базе данных; верхнее поле Memo предназначено для отображения текста СМС; нижнее поле Memo предназначено для отображения AT-команд, которые будут поступать на модем передатчик через Com-порт; два ComboBox рядом с текстом Com port предназначены для выбора com порта, к которому подключен модем - передатчик и установки скорости обмена по выбранному Com – порту; текстовое поле Nb chars предназначено для отображения размера СМС в байтах, если размер СМС больше 140 байт, то формируются две СМС и размер их отображается в текстовых полях Nb chars1 и Nb chars2; кнопка Send sms формирует AT команды, необходимые для отправки СМС.
Вот что у меня получилось:
Теперь напишем функции для работы с Сом портом. В основном используются те же функции, что и для работы с файлами: CreateFile, WriteFile, ReadFile. Используя их напишем функции для инициализации Сом порта, записи информации в Сом порт и чтения информации из Сом порта
Инициализация Com порта.
DCB dcb;
HANDLE hComm;
//Установка настроек com-порта
bool __fastcall InstallComm(AnsiString sPort="COM1",AnsiString Baud="115200" ) //номер порта
{
BOOL fSuccess;
char szCommPattern[8];
if (sPort=="")
return false;
sprintf(szCommPattern, "%s", sPort.c_str());
hComm = CreateFile(szCommPattern,
GENERIC_READ | GENERIC_WRITE,0,0,
OPEN_EXISTING,0,0);
if (hComm == INVALID_HANDLE_VALUE)
return false;
//Получаем настройки порта
fSuccess = GetCommState(hComm, &dcb);
if (!fSuccess)
return false;
dcb.BaudRate = StrToInt(Baud); //CBR_115200;
dcb.ByteSize = 8;
dcb.fParity = FALSE;
dcb.Parity = NOPARITY;
dcb.StopBits = TWOSTOPBITS;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity = FALSE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
//Устанавливаем новые настройки
fSuccess = SetCommState(hComm, &dcb);
if (!fSuccess)
return false;
//Устанавливаем размеры входного и выходного буфера данных
SetupComm(hComm, 1024, 1024);
/* Установка таймаутов.
Служит, чтобы программа не висла
в ожидании ответа от модема */
COMMTIMEOUTS TO;
TO.ReadIntervalTimeout = 80;
TO.ReadTotalTimeoutMultiplier = 1;
TO.ReadTotalTimeoutConstant = 100;
TO.WriteTotalTimeoutMultiplier = 200;
TO.WriteTotalTimeoutConstant = 2000;
SetCommTimeouts(hComm, &TO);
return true;
}
Запись в com-порт.
void WriteBuffer(char *buf_out)
{
unsigned long dwSize;
// char *buf_out="AT+CMGF=0\r";
GetCommState(hComm,&dcb);
dcb.fDtrControl=DTR_CONTROL_DISABLE;
SetCommState(hComm,&dcb);
GetCommState(hComm,&dcb);
dcb.fDtrControl=DTR_CONTROL_ENABLE;
SetCommState(hComm,&dcb);
WriteFile(hComm, buf_out, strlen(buf_out), &dwSize, NULL);
Sleep(1);
}
Для чтения из Сом порта просто будем вызывать функцию ReadFile
BOOL ReadFile(
HANDLE hFile, // handle of file to read
LPVOID lpBuffer, // address of buffer that receives data
DWORD nNumberOfBytesToRead, // number of bytes to read
LPDWORD lpNumberOfBytesRead, // address of number of bytes read
LPOVERLAPPED lpOverlapped // address of structure for data
);
Еще две функции относящиеся к Сом порту.
Эта просто создает список скоростей обмена в Сом порту для ComboBox2.
void TForm1::SpeedCOMPort(void)
{
ComboBox2->Items->Add("115200");
ComboBox2->Items->Add("57600");
ComboBox2->Items->Add("38400");
ComboBox2->Items->Add("19200");
ComboBox2->Items->Add("9600");
ComboBox2->Items->Add("4800");
ComboBox2->Items->Add("2400");
ComboBox2->Items->Add("1200");
ComboBox2->ItemIndex = 0;
}
А эта функция определяет, существует ли вообще у компьютера доступные Сом порты и создает из них список для ComboBox1.
void TForm1::DetectCOMport(void)
{
HANDLE hComm_test;
String comm_port;
for(int i = 1; i < 255; i++)
{
comm_port = "COM" + IntToStr(i);
hComm_test = CreateFile(comm_port.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
if(hComm_test != INVALID_HANDLE_VALUE)
{
ComboBox1->Items->Add("COM" + IntToStr(i));
CloseHandle(hComm_test);
}
}
if(ComboBox1->Items->Capacity>=1)
{
ComboBox1->ItemIndex = 0;
SpeedCOMPort();
}
else
{
ComboBox1->Text="";
ComboBox2->Text="";
}
}
Теперь надо написать функции преобразующие текст СМС в формат PDU с Классом 1, PID$7d и 8 битовым кодированием.
Преобразуем номер телефона:
AnsiString ConvertPhoneNumber(char* PhoneNumber)
{
char telefon[13];
char telefonConvert[13];
sprintf(telefon,"%s",PhoneNumber);
//Добавляем символ F в ASCII коде
telefon[strlen(telefon)]='\x46';
telefon[12]='\x00';
//Меняем четные и нечетные символы номера телефона местами
for(int i=0;i<12;i=i+2)
{
telefonConvert[i]=telefon[i+1];
telefonConvert[i+1]=telefon[i];
}
telefonConvert[12]='\x00';
return AnsiString(telefonConvert);
}
Функция преобразующая текст СМС в ASCII код.
AnsiString ConvertToMessageHex(AnsiString message)
{
AnsiString str="";
int integer;
char out[10];
char* var=new char[strlen(message.c_str())+2];
strcpy(var, message.c_str());
for(int i=0; i<strlen(var); i++)
{
integer=(int)var[i];
itoa(integer, out, 16);
str=str+AnsiString(out);
}
delete []var;
str=UpperCase(str);
return str;
}
Теперь создадим СМС используя PDU формат. Эту функцию используем если общий размер СМС меньше 140 байт.
AnsiString TForm1::CreateMessagePDU(void)
{
char* begin0011000B91="0011000B91";
char telefon[12];
char* afteTelefon="7d15AA";
AnsiString strOnePDU;
//шапка СМС , считываем выделенный номер телефона из ListBox1
sprintf(telefon,"%s",ListBox1->Items->Strings[ListBox1->ItemIndex].c_str());
strOnePDU=AnsiString(begin0011000B91)+ConvertPhoneNumber(telefon)+
AnsiString(afteTelefon);
//Сформируем полезный текст, подсчитаем его размер, все это представим ASCII символами, и добавим к уже созданной шапке
AnsiString MessageText="";
int b;
char* OTAP_IMPNG="OTAP_IMPNG";
char* PWD="PWD:";
char* APNORNUM="APNORNUM:";
char* NETUSER="NETUSER:";
char* NETPWD="NETPWD:";
char* DNS="DNS:";
char* JADURL="JADURL:";
char* APPDIR="APPDIR:";
char* BEARER="BEARER:gprs";
char* START="START:install";
int lenStr;
Memo2->Lines->Clear();
char* var1=new char[strlen(OTAP_IMPNG)+2];
strcpy(var1, OTAP_IMPNG);
b=strlen(var1);
var1[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var1))+"0A";
// 0A в ASCII коде LF перевод каретки на новую строку
delete []var1;
char* var2=new char[strlen((PWD+Edit4->Text).c_str())+2];
strcpy(var2, (PWD+Edit4->Text).c_str());
b=strlen(var2);
var2[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var2))+"0A";
delete []var2;
if(!(Edit5->Text.IsEmpty()))
{
char* var3=new char[strlen((APNORNUM+Edit5->Text).c_str())+2];
strcpy(var3, (APNORNUM+Edit5->Text).c_str());
b=strlen(var3);
var3[b+1]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var3))+"0A";
delete []var3;
}
if(!(Edit6->Text.IsEmpty()))
{
char* var9=new char[strlen((NETUSER+Edit6->Text).c_str())+2];
strcpy(var9, (NETUSER+Edit6->Text).c_str());
b=strlen(var9);
var9[b+1]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var9))+"0A";
delete []var9;
}
if(!(Edit7->Text.IsEmpty()))
{
char* var10=new char[strlen((NETPWD+Edit7->Text).c_str())+2];
strcpy(var10, (NETPWD+Edit7->Text).c_str());
b=strlen(var10);
var10[b+1]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var10))+"0A";
delete []var10;
}
if(!(Edit2->Text.IsEmpty()))
{
char* var4=new char[strlen((JADURL+Edit2->Text).c_str())+2];
strcpy(var4, (JADURL+Edit2->Text).c_str());
b=strlen(var4);
var4[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var4))+"0A";
delete []var4;
}
if(!(Edit3->Text.IsEmpty()))
{
char* var5=new char[strlen((APPDIR+Edit3->Text).c_str())+2];
strcpy(var5, (APPDIR+Edit3->Text).c_str());
b=strlen(var5);
var5[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var5))+"0A";
delete []var5;
}
char* var6=new char[strlen(BEARER)+2];
strcpy(var6, BEARER);
b=strlen(var6);
var6[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var6))+"0A";
delete []var6;
if(!(Edit8->Text.IsEmpty()))
{
char* var11=new char[strlen((DNS+Edit8->Text).c_str())+2];
strcpy(var11, (DNS+Edit8->Text).c_str());
b=strlen(var11);
var11[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var11))+"0A";
delete []var11;
}
char* var7=new char[strlen(START)+2];
strcpy(var7, START);
b=strlen(var7);
var7[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var7))+"0A";
delete []var7;
//Подсчитаем размер получившейся СМС
lenStr=strlen(MessageText.c_str())/2;
if(lenStr<140)
{
Memo2->Lines->Add(OTAP_IMPNG);
Memo2->Lines->Add(PWD+Edit4->Text);
if(!(Edit5->Text.IsEmpty())) Memo2->Lines->Add(APNORNUM+Edit5->Text);
if(!(Edit6->Text.IsEmpty())) Memo2->Lines->Add(NETUSER+Edit6->Text);
if(!(Edit7->Text.IsEmpty())) Memo2->Lines->Add(NETPWD+Edit7->Text);
if(!(Edit2->Text.IsEmpty())) Memo2->Lines->Add(JADURL+Edit2->Text);
if(!(Edit3->Text.IsEmpty())) Memo2->Lines->Add(APPDIR+Edit3->Text);
Memo2->Lines->Add(BEARER);
if(!(Edit8->Text.IsEmpty())) Memo2->Lines->Add(DNS+Edit8->Text);
Memo2->Lines->Add(START);
}
char lenHex[10];
//Число обозначающее размер надо представить в шестнадцатеричном виде.
itoa(lenStr, lenHex, 16);
strOnePDU=strOnePDU+UpperCase(AnsiString(lenHex))+MessageText;
//Вот и получили СМС в PDU формате, можно отправлять в СОМ порт.
return strOnePDU;
}
Если размер текста больше 140 байт, то сформируем две СМС:
bool TForm1::CreateMessagePDU_big(AnsiString& MessageFirst, AnsiString& MessageSecond)
{
char* begin0011000B91="0011000B91";
char telefon[12];
char* afteTelefon="7d15AA";
AnsiString strOnePDU;
bool first=false;
bool second=false;
//считывем выделенный номер телефона из ListBox1
sprintf(telefon,"%s",ListBox1->Items->Strings[ListBox1->ItemIndex].c_str());
strOnePDU=AnsiString(begin0011000B91)+ConvertPhoneNumber(telefon)+
AnsiString(afteTelefon);
//Возвращаем SMS сообщение одной строкой без пробелов
AnsiString MessageText="";
int b;
char* OTAP_IMPNG="OTAP_IMPNG";
char* PWD="PWD:";
char* APNORNUM="APNORNUM:";
char* NETUSER="NETUSER:";
char* NETPWD="NETPWD:";
char* DNS="DNS:";
char* JADURL="JADURL:";
char* APPDIR="APPDIR:";
char* BEARER="BEARER:gprs";
char* START="START:install";
int lenStr;
char* var1=new char[strlen(OTAP_IMPNG)+2];
strcpy(var1, OTAP_IMPNG);
b=strlen(var1);
var1[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var1))+"0A";
delete []var1;
char* var2=new char[strlen((PWD+Edit4->Text).c_str())+2];
strcpy(var2, (PWD+Edit4->Text).c_str());
b=strlen(var2);
var2[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var2))+"0A";
delete []var2;
if(!(Edit5->Text.IsEmpty()))
{
char* var3=new char[strlen((APNORNUM+Edit5->Text).c_str())+2];
strcpy(var3, (APNORNUM+Edit5->Text).c_str());
b=strlen(var3);
var3[b+1]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var3))+"0A";
delete []var3;
}
if(!(Edit6->Text.IsEmpty()))
{
char* var9=new char[strlen((NETUSER+Edit6->Text).c_str())+2];
strcpy(var9, (NETUSER+Edit6->Text).c_str());
b=strlen(var9);
var9[b+1]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var9))+"0A";
delete []var9;
}
if(!(Edit7->Text.IsEmpty()))
{
char* var10=new char[strlen((NETPWD+Edit7->Text).c_str())+2];
strcpy(var10, (NETPWD+Edit7->Text).c_str());
b=strlen(var10);
var10[b+1]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var10))+"0A";
delete []var10;
}
if(!(Edit2->Text.IsEmpty()))
{
char* var4=new char[strlen((JADURL+Edit2->Text).c_str())+2];
strcpy(var4, (JADURL+Edit2->Text).c_str());
b=strlen(var4);
var4[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var4))+"0A";
delete []var4;
}
lenStr=strlen(MessageText.c_str())/2;
char lenHex1[10];
itoa(lenStr, lenHex1, 16);
MessageFirst=strOnePDU+UpperCase(AnsiString(lenHex1))+MessageText;
//Первая СМС сформирована, проверяем чтобы размер был менее 140 байт
if(lenStr<140)
{
Memo2->Lines->Add(OTAP_IMPNG);
Memo2->Lines->Add(PWD+Edit4->Text);
if(!(Edit5->Text.IsEmpty())) Memo2->Lines->Add(APNORNUM+Edit5->Text);
if(!(Edit6->Text.IsEmpty())) Memo2->Lines->Add(NETUSER+Edit6->Text);
if(!(Edit7->Text.IsEmpty())) Memo2->Lines->Add(NETPWD+Edit7->Text);
if(!(Edit2->Text.IsEmpty())) Memo2->Lines->Add(JADURL+Edit2->Text);
first=true;
}
//----------------------------------------------------------
MessageText="";
char* var11=new char[strlen(OTAP_IMPNG)+2];
strcpy(var11, OTAP_IMPNG);
b=strlen(var11);
var11[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var11))+"0A";
delete []var11;
char* var12=new char[strlen((PWD+Edit4->Text).c_str())+2];
strcpy(var12, (PWD+Edit4->Text).c_str());
b=strlen(var12);
var12[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var12))+"0A";
delete []var12;
if(!(Edit3->Text.IsEmpty()))
{
char* var5=new char[strlen((APPDIR+Edit3->Text).c_str())+2];
strcpy(var5, (APPDIR+Edit3->Text).c_str());
b=strlen(var5);
var5[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var5))+"0A";
delete []var5;
}
char* var6=new char[strlen(BEARER)+2];
strcpy(var6, BEARER);
b=strlen(var6);
var6[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var6))+"0A";
delete []var6;
if(!(Edit8->Text.IsEmpty()))
{
char* var11=new char[strlen((DNS+Edit8->Text).c_str())+2];
strcpy(var11, (DNS+Edit8->Text).c_str());
b=strlen(var11);
var11[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var11))+"0A";
delete []var11;
}
char* var7=new char[strlen(START)+2];
strcpy(var7, START);
b=strlen(var7);
var7[b]='\x00';
MessageText=MessageText+
ConvertToMessageHex(AnsiString(var7))+"0A";
delete []var7;
lenStr=strlen(MessageText.c_str())/2;
char lenHex2[10];
itoa(lenStr, lenHex2, 16);
MessageSecond=strOnePDU+UpperCase(AnsiString(lenHex2))+MessageText;
//Вторая СМС сформирована, проверяем чтобы размер ее был меньше 140 байт
lenStr=strlen(MessageText.c_str())/2;
if(lenStr<140)
{
Memo2->Lines->Add(" ");
Memo2->Lines->Add(OTAP_IMPNG);
Memo2->Lines->Add(PWD+Edit4->Text);
if(!(Edit3->Text.IsEmpty())) Memo2->Lines->Add(APPDIR+Edit3->Text);
Memo2->Lines->Add(BEARER);
if(!(Edit8->Text.IsEmpty())) Memo2->Lines->Add(DNS+Edit8->Text);
Memo2->Lines->Add(START);
second=true;
}
//Если размер и первой и второй СМС меньше 140 байт возвращаем TRUE, если нет FALSE, но по идее размера двух СМС должно хватить для передачи нужной нам информации.
if (first && second)
return true;
else
return false;
}
И вот здесь, для отправки СМС мы используем наши функции для работы с COM портом и функции создания СМС в формате PDU
void TForm1::SendSMSSmall(void)
{
if(FindIDSmsFile()==true)
{
int ibuf3;
char Data[2000];
int len;
char *buf1="AT+CMGF=0\r";
char *buf="AT+CMGS=";
AnsiString AT_pl_CMGS="";
len=strlen(CreateMessagePDU().c_str())/2-1;
AT_pl_CMGS=AT_pl_CMGS+AnsiString(buf)+CurrToStr(len)+"\r";
char* buf3=new char[strlen(CreateMessagePDU().c_str())+2];
strcpy(buf3,CreateMessagePDU().c_str());
Memo1->Lines->Clear();
unsigned long dwSize;
int a,b,c;
if(!InstallComm(ComboBox1->Text, ComboBox2->Text))
MessageBox(NULL, "Невозможно открыть последовательный порт", "Error", MB_OK);
else
{
WriteBuffer(buf1);
a=ReadFile( hComm, Data, 2000, &dwSize, NULL );
if(a>0)
{
Data[dwSize] = 0;
Memo1->Lines->Add(Data);
}//if(a>0)
CloseHandle(hComm);
} //else
//-----------------------------------------------------------------
Sleep(1000);
//--------------------------------------------------------------------
if(!InstallComm(ComboBox1->Text, ComboBox2->Text))
MessageBox(NULL, "Невозможно открыть последовательный порт", "Error", MB_OK);
else
{
WriteBuffer(AT_pl_CMGS.c_str());
b=ReadFile( hComm, Data, 2000, &dwSize, NULL );
if(b>0)
{
Data[dwSize] = 0;
Memo1->Lines->Add(Data);
}//if(b>0)
CloseHandle(hComm);
} //else
//-----------------------------------------------------------------------
Sleep(2000);
//------------------------------------------------------------------------
if(!InstallComm(ComboBox1->Text, ComboBox2->Text))
MessageBox(NULL, "Невозможно открыть последовательный порт", "Error", MB_OK);
else
{
ibuf3=strlen(buf3);
buf3[ibuf3]='\x1A';
// Символ ‘\x1A’ в ASCII символах SUB - конец файла или <ctrl-z>
WriteBuffer(buf3);
c=ReadFile( hComm, Data, 2000, &dwSize, NULL );
if (c>0)
{
Data[ibuf3] = 0;
Memo1->Lines->Add(Data);
}//if (c>0)
CloseHandle(hComm);
} //else
delete []buf3;
}//if(FindIDSmsFile()==true)
}
Тоже самое если информации много и необходимо посылать две СМС
void TForm1::SendSMSBig(void)
{
if(FindIDSmsFile()==true)
{
int ibuf3;
int ibuf4;
char Data[2000];
int len;
char *buf1="AT+CMGF=0\r";
char *buf="AT+CMGS=";
AnsiString AT_pl_CMGS="";
AnsiString One;
AnsiString Two;
bool yes;
yes=CreateMessagePDU_big(One, Two);
if(yes)
{
len=strlen(One.c_str())/2-1;
AT_pl_CMGS="";
AT_pl_CMGS=AT_pl_CMGS+AnsiString(buf)+CurrToStr(len)+"\r";
char* buf3=new char[strlen(One.c_str())+2];
strcpy(buf3,One.c_str());
Memo1->Lines->Clear();
unsigned long dwSize;
int a,b,c;
if(!InstallComm(ComboBox1->Text, ComboBox2->Text))
MessageBox(NULL, "Невозможно открыть последовательный порт", "Error", MB_OK);
else
{
WriteBuffer(buf1);
a=ReadFile( hComm, Data, 2000, &dwSize, NULL );
if(a>0)
{
Data[dwSize] = 0;
Memo1->Lines->Add(Data);
}//if(a>0)
CloseHandle(hComm);
}//else
//-----------------------------------------------------------------------------
Sleep(1000);
//----------------------------------------------------------------------------
if(!InstallComm(ComboBox1->Text, ComboBox2->Text))
MessageBox(NULL, "Невозможно открыть последовательный порт", "Error", MB_OK);
else
{
WriteBuffer(AT_pl_CMGS.c_str());
b=ReadFile( hComm, Data, 2000, &dwSize, NULL );
if(b>0)
{
Data[dwSize] = 0;
Memo1->Lines->Add(Data);
}//if(b>0)
CloseHandle(hComm);
} //else
//------------------------------------------------------------------------------
Sleep(2000);
//-----------------------------------------------------------------------------
if(!InstallComm(ComboBox1->Text, ComboBox2->Text))
MessageBox(NULL, "Невозможно открыть последовательный порт", "Error", MB_OK);
else
{
ibuf3=strlen(buf3);
buf3[ibuf3]='\x1A';
// Символ ‘\x1A’ в ASCII символах SUB - конец файла или <ctrl-z>
WriteBuffer(buf3);
c=ReadFile( hComm, Data, 2000, &dwSize, NULL );
if (c>0)
{
Data[ibuf3] = 0;
Memo1->Lines->Add(Data);
}//if (c>0)
CloseHandle(hComm);
} //else
delete []buf3;
//============================================================================
Sleep(10000);
//============================================================================
len=strlen(Two.c_str())/2-1;
AT_pl_CMGS="";
AT_pl_CMGS=AT_pl_CMGS+AnsiString(buf)+CurrToStr(len)+"\r";
char* buf4=new char[strlen(Two.c_str())+2];
strcpy(buf4,Two.c_str());
if(!InstallComm(ComboBox1->Text, ComboBox2->Text))
MessageBox(NULL, "Невозможно открыть последовательный порт", "Error", MB_OK);
else
{
WriteBuffer(buf1);
a=ReadFile( hComm, Data, 2000, &dwSize, NULL );
if(a>0)
{
Data[dwSize] = 0;
Memo1->Lines->Add(Data);
}//if(a>0)
CloseHandle(hComm);
} //else
//-----------------------------------------------------------------------------
Sleep(1000);
//-----------------------------------------------------------------------------
if(!InstallComm(ComboBox1->Text, ComboBox2->Text))
MessageBox(NULL, "Невозможно открыть последовательный порт", "Error", MB_OK);
else
{
WriteBuffer(AT_pl_CMGS.c_str());
b=ReadFile( hComm, Data, 2000, &dwSize, NULL );
if(b>0)
{
Data[dwSize] = 0;
Memo1->Lines->Add(Data);
}//if(b>0)
CloseHandle(hComm);
} //else
//-----------------------------------------------------------------------------
Sleep(2000);
//-----------------------------------------------------------------------------
if(!InstallComm(ComboBox1->Text, ComboBox2->Text))
MessageBox(NULL, "Невозможно открыть последовательный порт", "Error", MB_OK);
else
{
ibuf4=strlen(buf4);
buf4[ibuf4]='\x1A';
// Символ ‘\x1A’ в ASCII символах SUB - конец файла или <ctrl-z>
WriteBuffer(buf4);
c=ReadFile( hComm, Data, 2000, &dwSize, NULL );
if (c>0)
{
Data[ibuf4] = 0;
Memo1->Lines->Add(Data);
}//if(c>0)
CloseHandle(hComm);
}//else
delete []buf4;
}// if(yes)
}//if(FindIDSmsFile()==true)
}
Во время работы программы я буду использовать два вспомогательных файла. В одном будет список телефонов, а в другом параметры, передаваемые в СМС. Объединятся эта информация будет через идентификатор.
//Функция обработки события: нажатия кнопки Записать (Button6)
void __fastcall TForm1::Button6Click(TObject *Sender)
{
bool numOK=true;
char telefon[13];
char Id_str[10];
int Id;
int len;
FILE *telef;
// Индентификатор переводится из AnsiString в char и копируется в переменную Id_str
sprintf(Id_str,"%s",Edit9->Text.c_str());
//Проверяем чтобы состоял из одних только цифр
int a=10;
for(int i=0;i<a-1;i++)
{
if(Id_str[i]=='\x00')
{
Id=Edit9->Text.ToInt();
break;
}
else if(!(('\x30'<=Id_str[i])&&(Id_str[i]<='\x39')))
{
MessageBox(NULL,"Идентификатор должен состоять из цифр!", "Error", MB_OK);
numOK=false;
break;
}
else if(('\x30'<=Id_str[i])&&(Id_str[i]<='\x39'))
{
continue;
}
}
//Если идентификатор состоит из цифр, то проверяем чтобы не было второго такого же.
if (numOK==true)
{
FILE *id_telef;
int id_file;
char telefon_file[13];
if ((id_telef = fopen("telefon.dat", "rb"))!=NULL)
{
while (fscanf(id_telef, "%d %s",&id_file, telefon_file)!=EOF)
{
if (id_file==Id)
{
numOK=false;
MessageBox(NULL,"Телефон с таким идентификатором уже существует!", "Error", MB_OK);
break;
}
}
fclose(id_telef);
}
}
//Теперь т. к. идентификатор проверен и соответствует нашим требованиям займемся номером телефона.
if (numOK==true)
{
sprintf(telefon,"%s",Edit1->Text.c_str());
//В номере телефона используем только цифры, даже если первый символ в номере телефона “ +”.
if (telefon[0]=='\x2B')
{
// MessageBox(NULL, "Нашел +", "Error", MB_OK);
int n=13;
for(int i=0;i<n-1;i++)
{
telefon[i]=telefon[i+1];
}
telefon[13]='\x00';
}
len=strlen(telefon);
//Проверяем чтобы номер телефона состоял из 11 цифр
if (len!=11)
{
MessageBox(NULL,"Номер телефона должен состоять из 11 цифр!", "Error", MB_OK);
numOK=false;
}
else
{
int n=12;
for(int i=0;i<n-1;i++)
{
if(!(('\x30'<=telefon[i])&&(telefon[i]<='\x39')))
{
MessageBox(NULL,"В номере телефона должны быть только цифры!", "Error", MB_OK);
numOK=false;
break;
}
}
// Если номер телефона тоже прошел проверку, то записываем идентификатор и номер телефона в файл.
if (numOK==true)
{
ListBox1->Items->Add(telefon);
ListBox2->Items->Add(CurrToStr(Id));
Edit1->Text="";
Edit9->Text="";
if((telef = fopen("telefon.dat", "ab"))
== NULL)
{
MessageBox(NULL, "Не могу записать в файл", "Error", MB_OK);
}
else
{
fprintf(telef,"%d %s\r", Id, telefon);
fclose(telef);
}
} //if (numOK==true)
} // else if (len!=11)
} // (numOK==true)
}
//---------------------------------------------------------------------------
//При запуске программы мы считываем идентификаторы и соответствующие им номера телефонов из файла и заполняем этими списками ListBox1 и ListBox2. Также определяем номера доступных Com портов.
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Label13->Visible=False;
Label14->Visible=False;
Label15->Visible=False;
Label16->Visible=False;
Label10->Visible=False;
Label11->Visible=False;
FILE *telef;
int Id;
char telefon[12];
if ((telef = fopen("telefon.dat", "rb"))!=NULL)
{
while (fscanf(telef, "%d %s",&Id, telefon)!=EOF)
{
ListBox1->Items->Add(telefon);
ListBox2->Items->Add (CurrToStr(Id));
}
fclose(telef);
}
DetectCOMport();
}
//Чтобы удалить номер телефона надо его выделить в списке ListBox1 и нажать кнопку “Удалить”. Одновременно удалятся данные СМС связанные с этим номером через идентификатор.
void __fastcall TForm1::Button7Click(TObject *Sender)
{
if(ListBox1->ItemIndex<0)
{
ShowMessage("Вы не выбрали номер для удаления");
}
else
{
DellFileSMS();
DellFileTelefon();
}
}
//---------------------------------------------------------------------------
void TForm1::DellFileTelefon(void)
{
FILE *telef;
FILE *telef1;
int Id;
char telefon[12];
if ((telef = fopen("telefon.dat", "rb"))!=NULL)
{
if ((telef1 = fopen("telefon.tmp", "wb"))!=NULL)
{
while (fscanf(telef, "%d %s",&Id, telefon)!=EOF)
{
if (Id!=ListBox2->Items->Strings[ListBox2->ItemIndex].ToInt())
fprintf(telef1,"%d %s\r",Id, telefon);
//ListBox1->Items->Add(telefon);
}
fclose(telef1);
}
fclose(telef);
}
ListBox1->DeleteSelected();
ListBox2->DeleteSelected();
DeleteFile("telefon.dat");
MoveFile("telefon.tmp","telefon.dat");
}
void TForm1::DellFileSMS(void)
{
FILE *sms;
FILE *sms_tmp;
int Id_sms;
char smsPWD[20];
char smsAPNORNUM[20];
char smsNETUSER[20];
char smsNETPWD[20];
char smsDNS[20];
char smsJADURL[50];
char smsAPPDIR[50];
if ((sms = fopen("sms.dat", "rb"))!=NULL)
{
if ((sms_tmp = fopen("sms.tmp", "wb"))!=NULL)
{
while (fscanf(sms, "%d %s %s %s %s %s %s %s",&Id_sms, smsPWD,
smsAPNORNUM, smsJADURL, smsAPPDIR, smsNETUSER, smsNETPWD, smsDNS)!=EOF)
{
if (Id_sms!=ListBox2->Items->Strings[ListBox2->ItemIndex].ToInt())
fprintf(sms_tmp,"%d %s %s %s %s %s %s %s\r",Id_sms, smsPWD,
smsAPNORNUM, smsJADURL, smsAPPDIR, smsNETUSER, smsNETPWD, smsDNS);
//ListBox1->Items->Add(telefon);
}
fclose(sms_tmp);
}
fclose(sms);
}
Edit4->Text="";
Edit5->Text="";
Edit2->Text="";
Edit3->Text="";
Edit6->Text="";
Edit7->Text="";
Edit8->Text="";
Memo1->Lines->Clear();
Memo2->Lines->Clear();
// ListBox1->DeleteSelected();
// ListBox2->DeleteSelected();
DeleteFile("sms.dat");
MoveFile("sms.tmp","sms.dat");
}
bool TForm1::FindIDSmsFile(void)
{
FILE *sms;
int Id_sms;
char smsPWD[20];
char smsAPNORNUM[20];
char smsNETUSER[20];
char smsNETPWD[20];
char smsDNS[20];
char smsJADURL[50];
char smsAPPDIR[50];
bool IdSMSFind=false;
if ((sms = fopen("sms.dat", "rb"))!=NULL)
{
while (fscanf(sms, "%d %s %s %s %s %s %s %s",&Id_sms, smsPWD,
smsAPNORNUM, smsJADURL, smsAPPDIR, smsNETUSER, smsNETPWD, smsDNS)!=EOF)
{
if (Id_sms==ListBox2->Items->Strings[ListBox2->ItemIndex].ToInt())
{
IdSMSFind=true;
break;
}
}
fclose(sms);
}
return IdSMSFind;
}
//Чтобы создать СМС необходимо выбрать номер телефона, ввести данные в поля
void __fastcall TForm1::Button9Click(TObject *Sender)
{
if(ListBox1->ItemIndex<0)
{
ShowMessage("Вы не выбрали номер телефона!");
}
else
{
FILE *sms;
FILE *sms_tmp;
int Id_sms;
char smsJADURL[50];
char smsAPPDIR[50];
char smsPWD[20];
char smsAPNORNUM[20];
char smsNETUSER[20];
char smsNETPWD[20];
char smsDNS[20];
if(Edit4->Text.IsEmpty()) sprintf(smsPWD,"%s","empty");
else sprintf(smsPWD,"%s",Edit4->Text.c_str());
if(Edit5->Text.IsEmpty()) sprintf(smsAPNORNUM,"%s","empty");
else sprintf(smsAPNORNUM,"%s",Edit5->Text.c_str());
if (Edit2->Text.IsEmpty()) sprintf(smsJADURL,"%s","empty");
else sprintf(smsJADURL,"%s",Edit2->Text.c_str());
if (Edit3->Text.IsEmpty()) sprintf(smsAPPDIR,"%s","empty");
else sprintf(smsAPPDIR,"%s",Edit3->Text.c_str());
if (Edit6->Text.IsEmpty()) sprintf(smsNETUSER,"%s","empty");
else sprintf(smsNETUSER,"%s",Edit6->Text.c_str());
if (Edit7->Text.IsEmpty()) sprintf(smsNETPWD,"%s","empty");
else sprintf(smsNETPWD,"%s",Edit7->Text.c_str());
if (Edit8->Text.IsEmpty()) sprintf(smsDNS,"%s","empty");
else sprintf(smsDNS,"%s",Edit8->Text.c_str());
char smsJADURLfile[50];
char smsAPPDIRfile[50];
char smsPWDfile[20];
char smsAPNORNUMfile[20];
char smsNETUSERfile[20];
char smsNETPWDfile[20];
char smsDNSfile[20];
// char* smsBEARER="BEARER:gprs";
// char* smsSTART="START:install";
if ((sms = fopen("sms.dat", "rb"))==NULL)
{
if(( sms_tmp = fopen("sms.dat", "wb"))!=NULL)
{
Id_sms=ListBox2->Items->Strings[ListBox2->ItemIndex].ToInt();
fprintf(sms_tmp,"%d %s %s %s %s %s %s %s\r",Id_sms, smsPWD, smsAPNORNUM,
smsJADURL, smsAPPDIR, smsNETUSER, smsNETPWD, smsDNS);
}
fclose(sms_tmp);
}
else
{
//ShowMessage("Файл открыт!");
bool flFindSms=false;
sms_tmp = fopen("sms.tmp", "wb");
while (fscanf(sms, "%d %s %s %s %s %s %s %s",&Id_sms, smsPWDfile, smsAPNORNUMfile,
smsJADURLfile, smsAPPDIRfile, smsNETUSERfile, smsNETPWDfile, smsDNSfile)!=EOF)
{
if (Id_sms!=ListBox2->Items->Strings[ListBox2->ItemIndex].ToInt())
fprintf(sms_tmp,"%d %s %s %s %s %s %s %s\r",Id_sms, smsPWDfile,
smsAPNORNUMfile, smsJADURLfile, smsAPPDIRfile, smsNETUSERfile, smsNETPWDfile, smsDNSfile);
else
{
fprintf(sms_tmp,"%d %s %s %s %s %s %s %s\r",ListBox2->Items->Strings[ListBox2->ItemIndex].ToInt(), smsPWD,
smsAPNORNUM, smsJADURL, smsAPPDIR, smsNETUSER, smsNETPWD, smsDNS);
flFindSms=true;
}
}
if(flFindSms==false)
{
fprintf(sms_tmp,"%d %s %s %s %s %s %s %s\r",ListBox2->Items->Strings[ListBox2->ItemIndex].ToInt(), smsPWD,
smsAPNORNUM, smsJADURL, smsAPPDIR, smsNETUSER, smsNETPWD, smsDNS);
flFindSms=true;
}
fclose(sms_tmp);
fclose(sms);
DeleteFile("sms.dat");
MoveFile("sms.tmp","sms.dat");
}
MakeSmsPDU();
} //else
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ListBox1Click(TObject *Sender)
{
if(ListBox1->ItemIndex>=0)
{
ListBox2->ItemIndex=ListBox1->ItemIndex;
ShowMessageText();
MakeSmsPDU();
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ListBox2Click(TObject *Sender)
{
if(ListBox2->ItemIndex>=0)
{
ListBox1->ItemIndex=ListBox2->ItemIndex;
ShowMessageText();
MakeSmsPDU();
}
}
//---------------------------------------------------------------------------
Если внимательно посмотреть на текст передаваемой СМС, то увидим, что в поле JADURL указан путь к файлу *.jad. Но сама java программа это файл *.jar. Как же модем узнает откуда брать файл *.jar? Откроем файл *.jad в любом текстовом редакторе.
MIDlet-Jar-Size: 1058
MIDlet-Jar-URL: MyTC65.jar
MIDlet-Name: MyTC65 Midlet Suite
MIDlet-Vendor: Midlet Suite Vendor
MIDlet-Version: 1.0.0
MicroEdition-Configuration: CLDC-1.1
MicroEdition-Profile: IMP-2.0
Обратим внимание на строку MIDlet-Jar-URL: Именно здесь указывается полный путь к *.jar файлу. Если оба файла *.jad и *.jar будут лежать в корневом каталоге на сервере www.csu-tst.narod.ru, то эта строка должна выглядеть следующим образом:
MIDlet-Jar-URL: http://www.csu-tst.narod.ru/MyTC65.jar
Если требуется определить причину неудачи при использовании OTAP функции, то воспользуемся командой: AT^SCFG=Trace/Syslog/OTAP,1 С помощью нее модем трассирует весь процесс с выводом информации в стандартный порт вывода, определяемый параметром "Userware/Stdout". Например команда AT^SCFG="Userware/Stdout","ASC0" назначает стандартным портом вывода модема нулевой COM порт, разъем его находится на корпусе модема.
Команда AT^SJOTAP позволяет установить пароль на использование OTAP. Очевидно, что при этом для успешного выполнения OTAP функции в СМС необходимо указать этот пароль. В программе это поле “Password”. Команда AT^SJOTAP дает возможность также установить ограничения и по другим признакам.
Архив рассмотренной программы с исходниками: Send_SMS_only.rar
Кому интересно: ссылка на сайт посвященный той же теме: