- •Методичні вказівки
- •Лабораторна робота №5 Розробка програмного забезпечення для управління gsm-модемом. Передача та прийом даних через com-порт за допомогою радіомодулів.
- •5.1. Мета роботи
- •5.2. Теоретичні відомості
- •5.2.1. Основні відмомсті про gsm
- •5.2.2 Розробка программного забезпечення
- •Написати в події OnClick кнопки надіслати смс прописати наступну функцію
- •5.3. Програма роботи
- •5.4. Обладнання та програмне забезпечення
- •5.5. Порядок виконання роботи
- •Лістинг ініціалізації com-портру має наступний вигляд
- •5.6. Контрольні запитання.
5.3. Програма роботи
Ознайомитися з теоритичними відомостями та повторити АТ-командами з минулої лабораторної роботи. Розробити програму для управління GSM-модемом на мові програмування C++ з можливостями ручної та автоматичної передачі СМС-повідомлень вказаним абонентам, здійснення вихідних дзвінків.
5.4. Обладнання та програмне забезпечення
Персональний комп’ютер.
Середовище програмування Builder C++.
GSM-модем SIM900.
5.5. Порядок виконання роботи
Запустити програму HiperTerminal Пуск-Все програми- Стандартные- HiperTerminal
Вписати і передати команду для здійснення дзвінка на власний телефон.
Вписати і передати команду для відправки СМС
Відобразити знімки екрану та номер телефону, з якого був здійснений дзвінок у звіті.
Запустити середовище програмування Borland C++ Builder
Зберегти створений проект в папці з назвою GSM-модем
Змінити властивість Caption елемента Form1 на «управління GSM-модемом (Прізвище і ініціали)».
Додати на головну форму два елементи GroupBox з вкладки Standart, в GroupBox1 додати два секстових поля Edit, два статичних рядка Label багаторядкове текстове поле Memo та дві кнопки Button, в GroupBox2 відповідно 3 текстових поля Edit 3 рядка Label і дві кнопки Button. Розмістити вищезазначені елементи як вказано на рис. 1
Рис.1 Початковий вигляд головного вікна програми «Управління GSM»
Назвати всі елементи як вказано на рис. 2, а текстові поля очистити.
Рис.2 Головне вікно з перейменованими елементами
Додати до головного вікна програми ще один елемент GroupBox, назвати його «Журнал повідомлень» і розмістити в ньому елемент RichEdit.
Оголосити глобальні змінні головної форми
DCB dcb;
BOOL fSuccess;
BYTE chWriteBuff[1024];
OVERLAPPED Sync;
DWORD dwWrite;
AnsiString str_buf;
bool smssend=false;
Організувати ініціалізацію COM-портру при запуску програми (подія створення ворми fastcall TForm1::TForm1(TComponent* Owner))
Лістинг ініціалізації com-портру має наступний вигляд
TRegistry *Reg;
TStrings *Ts;
Reg= new TRegistry;
Ts= new TStringList;
Reg->RootKey=HKEY_LOCAL_MACHINE;
AnsiString key="HARDWARE\\DEVICEMAP\\SERIALCOMM";
Reg->OpenKey(key,false);
Reg->GetValueNames(Ts);
Ts->Free();
Reg->CloseKey();
Reg->Free();
hCOM=CreateFile("COM1",GENERIC_WRITE | GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(hCOM!=INVALID_HANDLE_VALUE)
RichEdit1->Lines->Append(">COM OK: Порт Відкрито");
else
RichEdit1->Lines->Append(">COM ERROR: Помилка відкриття порту");
fSuccess=PurgeComm(hCOM,PURGE_TXCLEAR|PURGE_RXCLEAR);
if(!fSuccess)
RichEdit1->Lines->Append(">COM ERROR: Помилка отримання маски порту");
fSuccess=GetCommState(hCOM,&dcb);
if(!fSuccess)
RichEdit1->Lines->Append(">COM ERROR: Помилка GetCommState");
dcb.BaudRate=CBR_9600;
dcb.ByteSize=8;
dcb.Parity=NOPARITY;
dcb.StopBits=ONESTOPBIT;
fSuccess=SetCommState(hCOM,&dcb);
if(!fSuccess)
RichEdit1->Lines->Append(">COM ERROR: Помилка SetCommState");
Створимо модуль для потоку. Для цього виберіть пункт меню File | New | Other для відкриття вікна створення нового модуля(рис.1).
Рис.1.Відкриття вікна створення нового модуля
Знайдіть у цьому вікні на вкладці New пункт Thread Object. Виділите його й натисніть кнопку ОК (рис2.).
Рис.2. Вибір пункта Thread Object
У результаті з'явиться вікно, показане на рис. 3. У цьому вікні потрібно вказати ім'я потоку який створюється . Назвемо потік - TReadThread.
Рис. 3. Задаєм ім'я потоку
В результаті Builder C++ автоматично згенерує 2 файли Unit2.cpp і Unit2.h . Переключатись між ними можна натискаючи на вкладки як показано на рисунку 4.
Рис.4. Вікно з текстом програми потоку
Збережемо дані файли зі стандартними іменами (Unit2.cpp і Unit2.h) і перейдемо до розгляду коду.
Перейдемо на вкладку Unit2.h і розглянемо код:
//---------------------------------------------------------------------------
#ifndef Unit2H
#define Unit2H
//---------------------------------------------------------------------------
#include <Classes.hpp>
//---------------------------------------------------------------------------
class TReadThread : public TThread
{
private:
protected:
void __fastcall Execute();
public:
__fastcall TReadThread(bool CreateSuspended);
};
//---------------------------------------------------------------------------
#endif
Перейдемо до розділу private і пропишемо змінні та методи:
DWORD dwRead;
BYTE *Buf;
void __fastcall DoRead(void);
Перейдемо тепер до файлу Unit2.cpp (права вкладка на рис4.) :
//---------------------------------------------------------------------------
// Important: Methods and properties of objects in VCL can only be
// used in a method called using Synchronize, for example:
//
// Synchronize(UpdateCaption);
//
// where UpdateCaption could look like:
//
// void __fastcall TReadThread::UpdateCaption()
// {
// Form1->Caption = "Updated in a thread";
// }
//---------------------------------------------------------------------------
__fastcall TReadThread::TReadThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
void __fastcall TReadThread::Execute()
{
//---- Place thread code here ----
}
//---------------------------------------------------------------------------
Перш за все нам неохідно підключити модуль форму основної програми, для цього необхідно написати #include “Unit1.h” одразу після #include “Unit2.h” (код виділений червоним шрифтом в наступному лісингу).
Всі рядки котрі розпочинаються з символу // закоментовані , тобто на них не звертаємо уваги. Поки нас цікавить функція Execute() (виділена ширним шрифтом). Як вже зазначалось вище, в тілі функції (між { } ) ми пишемо код, який буде виконувати наш потік (тобто вставимо сюди код який ми писали для кнопки «Зчитати з порту» на минулій лабораторній):
{
HANDLE hPort;
COMSTAT CommStat;
DWORD dwError;
DWORD dwRead;
BYTE *Buf;
OVERLAPPED OverRead;
int i;
AnsiString STR;
hPort = Form1->GetPortHandle();
if (!SetCommMask (hPort, EV_RXCHAR))
{
ShowMessage("ПОМИЛКА ВСТАНОВЛЕННЯ МАСКИ ПОРТУ");
};
OverRead.hEvent = CreateEvent(NULL, True, False, NULL);
if (OverRead.hEvent == Null)
{ShowMessage("Помилка при спробі зчитування порту");}
if(!ClearCommError(hPort,&dwError,&CommStat)) {
MessageBox(NULL,"Помилка очищення порта","Помилка",MB_ICONERROR);}
dwRead = CommStat.cbInQue;
if (dwRead > 0)
{
Buf = new BYTE[dwRead];
if (!ReadFile(hPort,Buf,dwRead,&dwRead,&OverRead))
{
MessageBox(NULL,"Помилка читання порта","Помилка",MB_ICONERROR);
}
// В chReadBuff знаходяться прочитані байти
// Далі йде обробка отриманих байтів
for(i=0; i<dwRead; ++i)
{
STR=STR+char(Buf[i]);/* IntToStr(short(Buf[i])) + " "; */
}
Synchronize(DoRead);
delete Buf;
}
В результаті файл Unit2.cpp буде виглядати наступним чином (код який потрібно дописати виділений жирним шрифтом):
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit2.h"
#include "Unit1.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// Important: Methods and properties of objects in VCL can only be
// used in a method called using Synchronize, for example:
//
// Synchronize(UpdateCaption);
//
// where UpdateCaption could look like:
//
// void __fastcall TReadThread::UpdateCaption()
// {
// Form1->Caption = "Updated in a thread";
// }
//---------------------------------------------------------------------------
__fastcall TReadThread::TReadThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
void __fastcall TReadThread::Execute()
{
HANDLE hPort;
COMSTAT CommStat;
DWORD dwError;
OVERLAPPED OverRead;
int i;
AnsiString STR;
FreeOnTerminate = true;
hPort = Form1->GetPortHandle();
if (!SetCommMask (hPort, EV_RXCHAR))
{
ShowMessage("ПОМИЛКА ВСТАНОВЛЕННЯ МАСКИ ПОРТУ");
};
OverRead.hEvent = CreateEvent(NULL, True, False, NULL);
if (OverRead.hEvent == Null)
{ShowMessage("Помилка при спробі зчитування порту");}
if(!ClearCommError(hPort,&dwError,&CommStat)) {
MessageBox(NULL,"Помилка очищення порта","Помилка",MB_ICONERROR);}
dwRead = CommStat.cbInQue;
if (dwRead > 0)
{
Buf = new BYTE[dwRead];
if (!ReadFile(hPort,Buf,dwRead,&dwRead,&OverRead))
{
MessageBox(NULL,"Помилка читання порта","Помилка",MB_ICONERROR);
}
Synchronize(DoRead);
delete Buf;
}
}//---------------------------------------------------------------------------
Підключим модуль потуку до коду основної програми, та об’явимо об’єкт класу TReadThread як показано на рисунку 5:
Рис.5
Після коду відкриття COM-порту допишемо
potik1 = new TReadThread(true);
potik1->Resume();
potik1->Priority = tpLower;
В події OnExit головної форми впишемо
potik1->Terminate();
Вкінці файлу Unit1.cpp допишемо:
HANDLE __fastcall TForm1::GetPortHandle(void)
{
return this->hPort;
}
У файлі Unit1.h у розділі оголошення public запишемо наступне
HANDLE __fastcall GetPortHandle(void);
Пояснення:
potik1 = new TReadThread(true); - створюємо новий об’єкт потоку.
potik1->Resume(); - запускаємо потік на виконання.
potik1->Terminate(); - зупинка та знищення потоку.
potik1->Priority = tpLower – задання приорітету для потоку.
Запрограмуємо кнопку «Передати АТ-команду» для цього двічі клікнемо по ній і впишемо наступний код
str_buf=Edit1->Text;
str_buf.cat_sprintf("\r");
WriteFile(hCOM,str_buf.c_str(),str_buf.Length(),&dwWrite,NULL);
Аналогічним чином запрограмуємо кнопку «Подзвонити».
if(Edit3->Text!=""){
AnsiString mes;
mes=">GSM Виклик "+Edit3->Text;
RichEdit1->Lines->Append(mes);
}
AnsiString str=Edit3->Text;
AnsiString call="ATD";
str.Delete(1,2);
str_buf =call+str+";";
str_buf.cat_sprintf("\r");
WriteFile(hCOM,str_buf.c_str(),str_buf.Length(),&dwWrite,NULL);
Запрограмуємо передачу СМС-повідомлення (кнопка передати СМС)
RichEdit1->Lines->Add(">GSM Відправка повідомлення");
AnsiString sms="AT+CMGS=";
str_buf =sms+"\""+"+"+Edit3->Text+"\"";
str_buf.cat_sprintf("\r");
WriteFile(hCOM,str_buf.c_str(),str_buf.Length(),&dwWrite,NULL);
smssend=true;
Після цього необхідно вписати в події ОnChange текстового рядка Edit2 наступне:
//Початок введення смс і його завершення
if(Edit2->Text==">" && smssend==true) {
AnsiString R;
str_buf="";
for(int i=0;i< Memo1->Lines->Count;i++) {
R=Memo1->Lines->Strings[i];
for(int j=1;j<R.Length()+1;j++) {
str_buf=str_buf+Memo1->Lines->Strings[i][j];
}}
str_buf.cat_sprintf("\r");
WriteFile(hCOM,str_buf.c_str(),str_buf.Length(),&dwWrite,NULL);
str_buf = char(26);
str_buf.cat_sprintf("\r");
WriteFile(hCOM,str_buf.c_str(),str_buf.Length(),&dwWrite,NULL);
}
Edit2->Text="\r";
Після цього в функції обробки потоку необхідно організувати виведення відповідей модема і прийом даних тоді Unit2.cpp буде мати вигляд
void __fastcall TReadThread::DoRead(void)
{
AnsiString ANS;
for(int i=0; i<dwRead; i++)
{
if(char(Buf[i])=='ш')Form1->RichEdit1->Lines->Add(">GSM Ввімкнення");
if(char(Buf[i])=='A' && char(Buf[i-1])=='E' && char(Buf[i-2])=='R')
{
Form1->RichEdit1->Lines->Add(">GSM Налаштовано");
Form1->Edit2->Text="GSM Налаштовано";
}
if(char(Buf[i])=='D' && char(Buf[i-1])=='T' && char(Buf[i-2])=='A'){Form1->RichEdit1->Lines->Add(">GSM Здійснення дзвінка");Form1->Edit4->Text="Здійснення дзвінка";}
if(char(Buf[i])=='Y' && char(Buf[i-1])=='S' && char(Buf[i-2])=='U'){ Form1->RichEdit1->Lines->Add(">GSM Абонент зайнятий");Form1->Edit4->Text="Абонент зайнятий";
if(char(Buf[i])=='A' && char(Buf[i-1])=='I' && char(Buf[i-2])=='D'){ Form1->RichEdit1->Lines->Add(">GSM Помилка виклику"); Form1->Edit4->Text="Помилка";
if(char(Buf[i])=='E' && char(Buf[i-1])=='I' && char(Buf[i-2])=='R'){ Form1->RichEdit1->Lines->Add(">GSM Завершення розмови"); Form1->Edit4->Text="Завершення розмови";
if(char(Buf[i])=='>' && Form1->smssend==true)Form1->Edit2->Text=">";
if(char(Buf[i])=='K' && Form1->smssend==true)
{
Form1->smssend=false;
Form1->RichEdit1->Lines->Add("СМС Відправлено");
Form1->Memo1->Clear();
Form1->Edit2->Text="Відправлено";
}
if(char(Buf[i])=='R' && char(Buf[i-1])=='O'&& char(Buf[i-2])=='R')Form1->Edit2->Text="Помилка СМС";
}
delete Buf;
}
Зкомпілювати і запустити програму на виконання
(бажано підігнати цю програму теж під конкретний процес, наприклад відіслав смс 5% і підіймається рівень води в башні на 5%, але це можна зробити і як 2 лаби)
1.Запустити середовище програмування Borland C++ Builder.
2.Відкрити проект попередньої лабораторної роботи (яку саме, тра чітко вказати )
3.Створити нову форму (тра їй дати назву, бо такк як далі ти пишеш Form1 не думаю, що буде саме вона)
4.Використовуючи об’єкти Shape та Image створити візуалізацію водонапірної башні як показано на рис. 2.
Рис.2 Візуалізація спустошення та випорожнення водонапірної башні
Рівень - показує потоне значення рівня води в резервуарі у відсотках
Завдання – значення завдання рівня води у відсотках
Швидкість – швидкість наповнення або спустошення башні (при значенні 1000 відсоток за секунду).
Верхня межа – вказує максимальний рівень води в башні.
Нижня межа – вказує нижній рівень води в башні.
5. Оголосити глобальні змінні
Лістинг:
int RV=100; \\поточний рівень води
int RVZ=50;\\ завдання на рівень води
int RVMAX=100;\\максимальний рівень
int RVMIN=0;\\мінімальний рівень
6. Запрограмувати кнопку +5%.
Лістинг:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if(RVZ<RVMAX)RVZ+=5;
}
7. Запрограмувати кнопку -5%.
Лістинг:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if(RVZ>RVMIN)RVZ-=5;
}
8. Додати до на форму об’єкт Timer1 і запрограмувати його наступним чином.
Лістинг:
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
if(RV>RVZ)RV--;
if(RV<RVZ)RV++;
Shape1->Height=RV*2;
Shape1->Top=32+200-Shape1->Height;
Edit1->Text=IntToStr(RV);
Edit2->Text=IntToStr(RVZ);
if(Edit3->Text!="")
Timer1->Interval=StrToInt(Edit3->Text);
}
9. Запустити програму на виконання та додати в звіт знімки екрану.
10. Використовуючи досвід, набутий при виконання попередніх лабораторних робіт , розробити алгоритм, в якому при прийому через COM-порт числа 100 рівень води буде збільшуватись на 5 %, а при прийомі числа 200 – зменшуватись (тут треба конкретніше).
Лістинг:
void __fastcall TReadThread::DoRead(void)
{
AnsiString STR;
if(Buf[0]==100)Form2->Button1->Click; \\ Натиснення +5%
if(Buf[0]==200)Form2->Button2->Click; \\ Натиснення -5%
delete Buf;
}
11. В звіті відобразити динаміку зміни рівня води в водонапірній башні і код програми для реалізації данного алгоритму.
