Как отправить SMS с помощью PIC16F628A и SIM900A
В данной статье я покажу вам один способ отправки SMS с помощью PIC16F628A и модуля SIMCOM SIM900A.
В предыдущей статье я выполнил обновление прошивки своего GSM модуля, чтобы он мог работать с европейскими (и российскими) сотовыми операторами. В той статье я кратко коснулся AT команд и инструкций.
В данной статье я сделаю еще один шаг вперед. Я отправлю SMS с помощью своего микроконтроллера PIC16F628A.
Требования
Чтобы получить максимальный результат от этой статьи, вам понадобятся:
- GSM модуль SIMCOM SIM900A с обновленной прошивкой, чтобы он мог зарегистрироваться у европейских (и российских) сотовых операторов;
- компьютер с MPLAB X и компилятором XC8;
- микроконтроллер PIC16F628A и микросхема MAX232;
- способ прошить свой PIC контроллер, я использую PICkit 3;
- макетная плата, перемычки и LCD дисплей;
- другие компоненты из перечня элементов.
Введение
При включении модуля SIM900A происходит множество событий. Одним из них является то, что модуль пытается зарегистрироваться в сети. После успешной регистрации мы можем отправлять SMS, получать SMS и получать имя сотового оператора (поставщика услуг). Основная цель данной статьи – отправить SMS на заранее определенный номер. Получение названия сотового оператора является просто дополнительным бонусом.
Модуль связывается с остальной схемой с помощью TTL или с помощью встроенной микросхемы MAX232. Это означает, что вся связь осуществляется с помощью UART микроконтроллера PIC, и все наши команды от PIC будут отправляться на порт UART с помощью printf
.
Аппаратное обеспечение
Я собрал макет по следующей схеме.
Поскольку я использую MAX232, я могу использовать её, чтобы обнаружить и устранить проблемы при передаче микроконтроллером GSM модулю. Для этого я отключаю GSM модуль, подключаю кабель для последовательного порта и открываю GtkTerm. Настройки передачи: 9600-8-N-1.
Это то, что микроконтроллер отправляет GSM модулю. Первые три строки служат только для проверки работоспособности связи через последовательный порт и для просмотра того, что я отправляю. Последние две строки являются инструкциями для GSM модуля.
Список компонентов
Количество | Компоненты | Номинал | Описание |
---|---|---|---|
2 | C1, C5 | 0,1 мкФ | Конденсатор |
4 | C2, C6, C7, C8 | 0,1 мкФ | Конденсатор, полярный |
1 | R1 | 1 кОм | Резистор |
1 | Q1 | 16 МГц | Кварцевый резонатор |
1 | JP4 | 16x2 LCD | Разъем |
2 | C3, C4 | 18 пФ | Конденсатор |
1 | R2 | 5 кОм | Потенциометр |
1 | JP1 | Вход 5 В стабилизированные | Разъем |
1 | JP2 | ICSP | Разъем |
1 | IC1 | MAX3232CPE | Приемопередатчик RS-232, напряжение питания 3,0–5,5 В |
1 | IC2 | PIC16F628F | 8-разрядный микроконтроллер с флэш-памятью |
1 | JP3 | модуль SIM900A | Разъем |
Программное обеспечение
Хотя программа снабжена комментариями, здесь я рассмотрю некоторые ее фрагменты. Когда PIC-контроллер включается, отображается короткое приветственное сообщение. Затем начинается 15-секундный отсчет. Я добавил отсчет по двум причинам:
- позволить GSM модулю зарегистрироваться в сети;
- визуально показать пользователю, что что-то происходит, и микроконтроллер работает.
При завершении обратного отсчета PIC-контроллер отправляет команду:
AT+CPOL?\r\n
Важно добавить символы возврата каретки и новой строки, \r\n
. Это говорит модулю о необходимости обработать текстовую строку, которая поступила в его буфер.
Затем модуль возвращает следующую строку:
+CPOL: 1,0,” N NetCom”,1,0,1
Это означает, что GSM модуль зарегистрирован в сети NetCom. NetCom – это название провайдера. Именно это название мы хотим отобразить на LCD дисплее. Теперь нам нужно извлечь это название из полученной строки. Способ, которым я это делаю, заключается в чтении всей строки в массив. Затем я ищу "какие-то_символы". Когда "какие-то_символы" найдены, я сохраняю их позиции в другом массиве. Назовем это стартом и стопом. Затем я использую значения старта и стопа, чтобы отобразить символы между ними на LCD дисплее. Возможно, это не самый изящный способ, но он прост.
Чтобы отправить SMS, я должен послать чуть больше AT команд.
Сначала я посылаю команду:
AT+CMGF=1\r\n
Это выбор формата SMS сообщения. 1 говорит модулю о необходимости перейти в текстовый режим.
Затем я посылаю команду:
AT+CMGS=”receiver”\r\n
“receiver” – это номер получателя, на который я хочу отправить сообщение.
Третьей я посылаю команду:
Message from PIC16F628A\r\n
А это само сообщение. Поскольку сообщение состоит из нескольких символов, включая пробелы, существует отличный способ сообщить модулю, где находится конец сообщения. Я должен послать символ EOF
, что и происходит при нажатии CTRL+Z.
printf(“%c”,26);
Это символ EOF
в обычно таблице ASCII. Теперь модуль отправит сообщение.
Полный код программы
// INCLUDES
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
// Конфигурация
#pragma config FOSC = HS // Биты выбора генератора (HS генератор: высокочастотный кварцевый резонатор на RA6/OSC2/CLKOUT и RA7/OSC1/CLKIN)
#pragma config WDTE = OFF // Бит включения сторожевого таймера (WDT выключен)
#pragma config PWRTE = OFF // Бит включения таймера запуска (PWRT выключен)
#pragma config MCLRE = OFF // Бит выбора функции вывода RA5/MCLR/VPP (вывод RA5/MCLR/VPP - цифровой вход, MCLR внутри подтянут к VDD)
#pragma config BOREN = OFF // Бит включения обнаружения снижения напряжения питания (BOD выключен)
#pragma config LVP = OFF // Бит включения низковольтного программирования (вывод RB4/PGM работает как цифровой вход/выход, для программирования должно использоваться высокое напряжение на MCLR)
#pragma config CPD = OFF // Бит защиты кода eeprom памяти данных (защита кода памяти данных выключена)
#pragma config CP = OFF // Бит защиты кода флэш памяти программ (защита кода выключена)
// Определения
#define _XTAL_FREQ 16000000 // опорная частота для компилятора
#define Rs PORTAbits.RA0 // Rs подключен к RA0
#define En PORTAbits.RA1 // En подключен к RA1
#define lcd_port TRISB // LCD на порт B
#define lcdLine1 0x00 // строка 1 начинается здесь
#define lcdLine2 0x40 // строка 2 начинается здесь
// Глобальные переменные
unsigned char ch, i;
unsigned char buffer[41];
unsigned char counter[2];
unsigned char receiver[] = "\"вставьте сюда свой номер телефона\"";
bool getchar_active = false;
// Прототипы функций
int lcd_strobe(void);
void lcd_write(unsigned char dat);
void lcd_cmd(unsigned char cmd);
void lcd_data(unsigned char dat);
void lcd_init(void);
void lcd_writestr(const unsigned char *c);
void lcd_setcursor(unsigned char loc);
void lcd_line1(void);
void lcd_line2(void);
void uart_init(void);
unsigned char getch(void);
void interrupt tc_int(void);
void notvaliddate(void);
void notvalidtime(void);
// функция импульса на выводе En
int lcd_strobe(void)
{
En = 1;
__delay_us(1);
En = 0;
}
// функция для записи в порт LCD
void lcd_write(unsigned char dat)
{
lcd_port &= 0x0f; // получить текущее состояние порта и очистить старшие биты
lcd_port |= (dat & 0xf0); // совместить старшие и младшие, оставить младшие
lcd_strobe();
lcd_port &= 0x0f;
lcd_port |= ((dat << 4) & (0xf0)); // совместить старшие и младшие
lcd_strobe();
__delay_ms(2); // небольшая задержка для обработки LCD
}
// функция для установки lcd в режим команд
void lcd_cmd(unsigned char cmd)
{
Rs = 0; // rs в состоянии лог. 0 для режима команд
lcd_write(cmd);
}
// функция для установки lcd в режим данных
void lcd_data (unsigned char dat)
{
Rs = 1; // rs в состоянии лог. 1 для режима данных
lcd_write(dat);
}
// функция для инициализации lcd
void lcd_init(void)
{
lcd_port &= 0x0f; // очистить старшие биты
lcd_port |= 0x30; // отправить данные на d7-4
Rs = 0;
lcd_strobe();
__delay_ms(10);
lcd_strobe();;
__delay_ms(10);
lcd_strobe();
__delay_ms(10);
lcd_port &= 0x0f; // очистить старшие биты
lcd_port |= 0x30; // отправить данные на d7-4
lcd_strobe();
__delay_ms(50);
lcd_cmd(0x28); // 4-бит, 2-строки, 5x7 шрифт
lcd_cmd(0x0e); // дисплей включить, курсор включить, мигание выключить
lcd_cmd(0x06); // автоинкремент, без сдвига
lcd_cmd(0x80); // адрес ddram с 0 смещение 80h
}
// функция для записи строки на lcd
void lcd_writestr(const unsigned char *c)
{
while (*c != '\0')
{
lcd_data(*c);
c++;
}
}
// функция для помещения курсора на lcd
void lcd_setcursor(unsigned char loc)
{
lcd_cmd(loc | 0x80);
}
// функция для перемещения курсора на первое место в первой строке
void lcd_line1(void)
{
lcd_setcursor(lcdLine1);
}
// функция для перемещения курсора на первое место во второй строке
void lcd_line2(void)
{
lcd_setcursor(lcdLine2);
}
// function to initialize the uart port
void uart_init(void)
{
TXSTAbits.BRGH = 0; // бит выбора высокой скорости, 1=высокая, 0=низкая
TXSTAbits.SYNC = 0; // бит выбора режима USART, 1=синхронный, 0=асинхронный
TXSTAbits.TX9 = 0; // бит выбора 9-бит, 1=9-битная передача, 0=8-битная передача
RCSTAbits.CREN = 1; // бит включения непрерывного приема, 1=включить непрерывный прием
/*
* 16 МГц
* 16000000 / 9600 = 1666.6666
* 1666.6666 / 64 = 26.0416
* 26.0416 - 1 = 25.0416
* 25.0416 = 25
*
* 16000000 / 4800 = 3333.3333
* 3333.3333 / 64 = 52.0833
* 52.0833 - 1 = 51.0833
* 51.0833 = 51
*/
SPBRG = 25; // 9600-n-8-1
PIE1bits.RCIE = 1; // бит включения прерывания по приему USART, 1=включен, 0=выключен
RCSTAbits.SPEN = 1; // бит включения последовательного порта, 1=включен, 0=выключен
TXSTAbits.TXEN = 1; // бит включения передачи, 1=включен, 0=выключен
return;
}
// функция для чтения символа из uart
unsigned char getch()
{
getchar_active = true;
while (getchar_active)
continue;
return RCREG;
}
void putch(unsigned char byte)
{
while (!TXSTAbits.TRMT);
TXREG = byte;
if ('\n' == byte)
{
while (!TXSTAbits.TRMT);
TXREG = '\r';
}
return;
}
// функция прерывания, вызывается при каждом полученном символе
void interrupt tc_int(void)
{
if (RCIF && RCIF)
{
getchar_active = false;
RCREG;
}
return;
}
void gsm_waitforsignal(void)
{
lcd_line1();
lcd_writestr("Getting provider");
for (i=14;i>0;i--)
{
lcd_line2();
lcd_writestr("Waiting... ");
itoa(counter, i, 10);
lcd_writestr(counter);
lcd_writestr(" ");
__delay_ms(1000);
}
lcd_line1();
lcd_writestr(" ");
lcd_line2();
lcd_writestr(" ");
printf("AT\r\n");
}
void gsm_getoperator(void)
{
char chartofind;
int j=0;
unsigned char pos[3];
int first, start, stop;
printf("AT+CPOL?\r\n");
ch = getch(); // прочитать \r
ch = getch(); // прочитать \n
ch = getch(); // прочитать +
if (ch == '+') // проверить, если у нас символ +, то это начало последовательности
{
printf("\r\n");
lcd_line1();
lcd_writestr("Operator ");
lcd_line2();
for (i=0;i<40;i++) // прочитать первые 40 символов после +
{
ch = getch(); // прочитать один символ
buffer[i] = ch; // поместить его в массив буфера
}
chartofind='"';
for (i=0;i<strlen(buffer);i++)
{
if (buffer[i] == chartofind)
{
pos[j] = i+1;
j++;
}
}
start = pos[0];
stop = pos[1];
for (i=start;i<stop-1;i++)
// {
// printf("%c", buffer[i]); // для отладки
//
// }
for (i=start;i<stop-1;i++)
{
lcd_data(buffer[i]);
}
}
}
void gsm_sendsms(void)
{
printf("AT+CMGF=1\r\n");
__delay_ms(200);
printf("AT+CMGS=");
printf(receiver);
printf("\r\n");
__delay_ms(200);
printf("Message from PIC16F628A\r\n");
__delay_ms(200);
printf("%c",26); // CTRL - Z
__delay_ms(5000);
lcd_line1();
lcd_writestr(" SMS SENT ");
lcd_line2();
lcd_writestr(" ");
__delay_ms(2000);
gsm_getoperator();
}
/* основной цикл программы */
void main(void)
{
CMCON = 0x07;
TRISA = 0b00000000;
TRISB = 0b00000110;
PORTA = 0b00000000;
PORTB = 0b00000000;
INTCONbits.PEIE = 1;
INTCONbits.GIE = 1;
lcd_strobe();
lcd_init();
uart_init();
lcd_writestr(" PIC 16F628A ");
lcd_line2();
lcd_writestr(" SIMCOM SIM900A ");
__delay_ms(2000);
printf("Testing PIC UART port.\r\n"); // для отладки
printf("PIC 16F628A.\r\n"); // для отладки
printf("SIMCOM SIM900A.\r\n"); // для отладки
gsm_waitforsignal();
__delay_ms(2000);
gsm_getoperator();
__delay_ms(2000);
gsm_sendsms();
while (1)
{
}
}
Заключение
Теперь вы можете получить название своего оператора связи и отправить SMS с помощью GSM модуля SIM900A.