Как отправить SMS с помощью PIC16F628A и SIM900A

Добавлено 18 марта 2019 в 08:39

В данной статье я покажу вам один способ отправки SMS с помощью PIC16F628A и модуля SIMCOM SIM900A.

В предыдущей статье я выполнил обновление прошивки своего GSM модуля, чтобы он мог работать с европейскими (и российскими) сотовыми операторами. В той статье я кратко коснулся AT команд и инструкций.

В данной статье я сделаю еще один шаг вперед. Я отправлю SMS с помощью своего микроконтроллера PIC16F628A.

Тестовый макет для отправки SMS с помощью PIC16F628A и SIM900A
Тестовый макет для отправки SMS с помощью PIC16F628A и SIM900A

Требования

Чтобы получить максимальный результат от этой статьи, вам понадобятся:

  • GSM модуль SIMCOM SIM900A с обновленной прошивкой, чтобы он мог зарегистрироваться у европейских (и российских) сотовых операторов;
  • компьютер с MPLAB X и компилятором XC8;
  • микроконтроллер PIC16F628A и микросхема MAX232;
  • способ прошить свой PIC контроллер, я использую PICkit 3;
  • макетная плата, перемычки и LCD дисплей;
  • другие компоненты из перечня элементов.

Введение

При включении модуля SIM900A происходит множество событий. Одним из них является то, что модуль пытается зарегистрироваться в сети. После успешной регистрации мы можем отправлять SMS, получать SMS и получать имя сотового оператора (поставщика услуг). Основная цель данной статьи – отправить SMS на заранее определенный номер. Получение названия сотового оператора является просто дополнительным бонусом.

Модуль связывается с остальной схемой с помощью TTL или с помощью встроенной микросхемы MAX232. Это означает, что вся связь осуществляется с помощью UART микроконтроллера PIC, и все наши команды от PIC будут отправляться на порт UART с помощью printf.

Аппаратное обеспечение

Я собрал макет по следующей схеме.

Схема макета для отправки SMS с помощью PIC16F628A и SIM900A
Схема макета для отправки SMS с помощью PIC16F628A и SIM900A

Поскольку я использую MAX232, я могу использовать её, чтобы обнаружить и устранить проблемы при передаче микроконтроллером GSM модулю. Для этого я отключаю GSM модуль, подключаю кабель для последовательного порта и открываю GtkTerm. Настройки передачи: 9600-8-N-1.

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

Это то, что микроконтроллер отправляет GSM модулю. Первые три строки служат только для проверки работоспособности связи через последовательный порт и для просмотра того, что я отправляю. Последние две строки являются инструкциями для GSM модуля.

Я не получил ответ от GSM модуля, так как он еще не подключен
Я не получил ответ от GSM модуля, так как он еще не подключен

Список компонентов

Перечень элементов
КоличествоКомпонентыНоминалОписание
2C1, C50,1 мкФКонденсатор
4C2, C6, C7, C80,1 мкФКонденсатор, полярный
1R11 кОмРезистор
1Q116 МГцКварцевый резонатор
1JP416x2 LCDРазъем
2C3, C418 пФКонденсатор
1R25 кОмПотенциометр
1JP1Вход 5 В стабилизированныеРазъем
1JP2ICSPРазъем
1IC1MAX3232CPEПриемопередатчик RS-232, напряжение питания 3,0–5,5 В
1IC2PIC16F628F8-разрядный микроконтроллер с флэш-памятью
1JP3модуль SIM900AРазъем

Программное обеспечение

Хотя программа снабжена комментариями, здесь я рассмотрю некоторые ее фрагменты. Когда PIC-контроллер включается, отображается короткое приветственное сообщение. Затем начинается 15-секундный отсчет. Я добавил отсчет по двум причинам:

  1. позволить GSM модулю зарегистрироваться в сети;
  2. визуально показать пользователю, что что-то происходит, и микроконтроллер работает.

При завершении обратного отсчета 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.

Фотографии и видео

Приветственное сообщение
Приветственное сообщение
Ожидание получения названия сотового оператора
Ожидание получения названия сотового оператора
Вывод названия сотового оператора
Вывод названия сотового оператора
Отправка SMS сообщения
Отправка SMS сообщения


На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.


Сообщить об ошибке