Отправка SMS с микроконтроллера

Для отправки смс через мобильный телефон, применяя микроконтроллер есть несколько проблем:
1. Конвертирование исходного сообщения в готовое для отправки
2. Сам процесс передачи подготовленного сообщения в мобильный телефон.

Для работы с мобильным телефоном или GSM модулем необходимо использовать набор команд, эти команды называются АТ команды. Синтаксис от телефона к телефону не сильно меняется, по сему можно ориентироваться что код будет работать на большинстве мобильных GSM телефонов.

1)Это набор макросов, они просты и реализуют работу АТ командами модуля SIM300, которые понимают большинство GSM модулей и мобильных телефонов.

  1. #define AT for(uint8_t i=0; i<sizeof(at); i++) usart0_write(at[i]); //Trasmit AT.
  2. #define ATE0 for(uint8_t i=0; i<sizeof(echo_off); i++) usart0_write(echo_off[i]);
  3. #define ATV1 for(uint8_t i=0; i<sizeof(atv_on); i++) usart0_write(atv_on[i]);
  4. #define ATV0 for(uint8_t i=0; i<sizeof(atv_off); i++) usart0_write(atv_off[i]);
  5. #define ATD for(uint8_t n=0; n<sizeof(atd); n++) usart0_write(atd[n]);
  6.  
  7. #define NUMBER0_UCS2 for(uint16_t n=ADDR_EEP_NUM0; n<(12+ADDR_EEP_NUM0);
  8. n++) number0_unicode(n);
  9. #define NUMBER1_UCS2 for(uint16_t n=ADDR_EEP_NUM1; n<(12+ADDR_EEP_NUM1);
  10. n++) number1_unicode(n);
  11. #define NUMBER2_UCS2 for(uint16_t n=ADDR_EEP_NUM2; n<(12+ADDR_EEP_NUM2);
  12. n++) number2_unicode(n);
  13. #define NUMBER3_UCS2 for(uint16_t n=ADDR_EEP_NUM3; n<(12+ADDR_EEP_NUM3);
  14. n++) number3_unicode(n);
  15. #define ENTER_ATD for(uint8_t n=0; n<sizeof(atd_enter);
  16. n++) usart0_write(atd_enter[n]);
  17. #define SMS for(uint8_t n=0; n<sizeof(sms_send);
  18. n++) usart0_write(sms_send[n]);
  19. #define CGMF1 for(uint8_t n=0; n<sizeof(format_text);
  20. n++) usart0_write(format_text[n]);
  21. #define ENTER_SMS for(uint8_t n=0; n<sizeof(sms_enter);
  22. n++) usart0_write(sms_enter[n]);
  23.  
  24. #define NUMBER0_GSM for(uint16_t n=ADDR_EEP_NUM0;
  25. n<(sizeof(number0)+ADDR_EEP_NUM0); n++) usart0_write(eeprom_read_byte(n));
  26. #define NUMBER1_GSM for(uint16_t n=ADDR_EEP_NUM1;
  27. n<(sizeof(number1)+ADDR_EEP_NUM1); n++) usart0_write(eeprom_read_byte(n));
  28. #define NUMBER2_GSM for(uint16_t n=ADDR_EEP_NUM2;
  29. n<(sizeof(number2)+ADDR_EEP_NUM2); n++) usart0_write(eeprom_read_byte(n));
  30. #define NUMBER3_GSM for(uint16_t n=ADDR_EEP_NUM3;
  31. n<(sizeof(number3)+ADDR_EEP_NUM3); n++) usart0_write(eeprom_read_byte(n));
  32.  
  33. #define END_SMS for(uint8_t n=0; n<sizeof(sms_end);
  34. n++) usart0_write(sms_end[n]); //Ctrl+Z
  35. #define CSCS_UCS2 for(uint8_t n=0; n<sizeof(te_format_ucs2);
  36. n++) usart0_write(te_format_ucs2[n]);
  37.  
  38. const char at[]={'A', 'T', 0x0D, 0x0A};
  39. const char echo_off[]={'A','T','E', '0', 0x0D, 0x0A};
  40. const char atv_off[]={'A','T','V', '0', 0x0D, 0x0A};
  41. const char atv_on[]={'A','T','V', '1', 0x0D, 0x0A};
  42. const char format_text[]={'A','T','+','C','M','G','F','=','1', 0x0D, 0x0A};
  43.  
  44. const char atd_enter[]={';', 0x0D, 0x0A};
  45. const char sms_enter[]={'"', 0x0D, 0x0A};
  46.  
  47. const char sms_end[]={0x1A};
  48. const char atd[4]={'A','T','D'};
  49.  
  50. const char sms_send[]={'A','T','+','C','M','G','S','=','"'};
  51.  
  52. const char te_format_ucs2[]={'A','T','+','C','S','C','S','=','"','U','C','S','2','"', 0x0D, 0x0A};

Здесь все просто, берем символы и отправляем по usart, за исключением, что усарт сделан как кольцевой буфер. Вообще это отдельная тема, которую надо затронуть, но о нем не сейчас, на чипеенабле есть статья и реализация его. Правда, эта функция моя, но все однотипно.
usart0_write(‘A’); – отправляет символ А в усарт, соответственно usart0_write(at[i]);- отправляет i-тый символ массива at.
Data=usart0_read(); - читает один байт данных из усарта.
len=usart0_rx_len(); - читает в пременную len количество принятых байт, если таковых нет функция вернет 0. Соответственно после отправки данных ответ мы ждем проверяя функцией usart0_rx_len();
если что то пришло, то возвращается количество принятых байт.
И две важные функции очистки буферов передатчика и приемника
usart0_clear_rx_buffer();
usart0_clear_tx_buffer();

Размеры этих буферов задаются в USART.h файле.
Также там задается формат посылки, скорость, четность и стоповые биты.
Про усарт все.

2) Переменные необходимые для отправки

  1. char number0[]EEMEM={'+','7','1','1','1','1','1','1,'1','1','1','1'};
  2. char number1[]EEMEM={'+','7','2','2','2','2','2','2','2','2','2','2'};
  3. char number2[]EEMEM={'+','7','3','3','3','3','3','3','3','3','3','3'};
  4. char number3[]EEMEM={'+','7','4','4','4','4','4','4','4','4','4','4'};
  5.  
  6. char text_sms0[]EEMEM="AБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЫЭЮЯабвгдежзийклмнопрстуфхцчшщьъыэюя000000";
  7. // Текст нашей 1 smsки.
  8. char text_sms1[]EEMEM="BБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЫЭЮЯабвгдежзийклмнопрстуфхцчшщьъыэюя111111";
  9. // Текст нашей 2 smsки.
  10. char text_sms2[]EEMEM="CБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЫЭЮЯабвгдежзийклмнопрстуфхцчшщьъыэюя222222";
  11. // Текст нашей 3 smsки.
  12. char text_sms3[]EEMEM="DБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЫЭЮЯабвгдежзийклмнопрстуфхцчшщьъыэюя333333";
  13. // Текст нашей 4 smsки.

Их тоже необходимо преобразовать в юникод. Номер преобразуется и отправляется в функции numberx_unicode(n); где n-соответствующая цифра в ASCII коде. x- номер телефонного номера из еепром Таким образом что бы преобразовать и передать весь номер нулеврго тел. нужно написать:

  1. for(uint16_t n=ADDR_EEP_NUM0; n<(sizeof(number0)+ADDR_EEP_NUM0); n++) number0_unicode(n);
  2. что и делает макрос NUMBER0_UCS2.

3) Преобразование и отправка номера телефона в функции происходит так:

  1. void number0_unicode(uint16_t num)
  2. {
  3. usart0_clear_tx_buffer(); //Clear Tx buffer.
  4. usart0_clear_rx_buffer(); //Clear Rx buffer.
  5.  
  6.  
  7. if(eeprom_read_byte(num)=='+')
  8. //Если в начале номера не цифра, а + (+7951xxxxxxx-например).
  9. {
  10. char plus[]="002B";
  11. for(uint8_t t1=0; t1<sizeof(plus); t1++) usart0_write(plus[t1]);
  12. }
  13. else
  14. {
  15. char not_plus[]="003";
  16. for(uint8_t t1=0; t1<sizeof(not_plus); t1++) usart0_write(not_plus[t1]);
  17.  
  18. usart0_write(eeprom_read_byte(num));
  19. }
  20. _delay_ms(100);
  21.  
  22. }

Здесь все просто за исключением знака +, его символ в юникоде 002В. Номера читаются из еепром.
Я рассчитываю, что ты знаешь о юникоде, что там отправляется один символ 4 байтами.

4) Преобразование и отправка непосредственно смс раскладывается в два этапа. Первое это преобразование символа в юникод и второе это преобразование уже юникода в hex символ.
Первый этап реализуется в функции text_unicode(uint16_t word);
В программе пишем:

  1. for(uint16_t i=0; i<len; i++) text_ucs2[i]=text_unicode(text_sms[i]);

где len соответственно длина нашей строки смс

text_ucs2[len]-это дву байтный массив строки смс кодированный юникодом.
Сама функция проста:

  1. int text_unicode(uint16_t word)
  2. {
  3. if(word>0x00BF)
  4. {
  5. word+=0x0350;
  6. }
  7. return word;
  8. }

Почему прибавляем 0х0350, так кириллица начинается с 0х0410 номера, а символ «А» это номер 0х0С. 0х0С+0х0350=0х0410, далее все символы идут по порядку, кроме буквы Ё, она к сожалению осталась в не удел, а жаль.

5) Второй этап, это преобразование юникода в хекс юникод.
Делаем это в функции text_unicode_hex(uint16_t word); делаем и отправляем один символ, а на деле отправляется 4 байта.

  1. for(uint16_t i=0; i<len; i++) text_unicode_hex(text_ucs2[i]);

Теперь что в самой функции:

  1. void text_unicode_hex(uint16_t word)
  2. {
  3. char data_tx[4];
  4. uint8_t byte;
  5.  
  6. byte=word>>12;
  7. byte=byte+0x30;
  8. data_tx[0]=byte;
  9.  
  10. byte=word>>8;
  11. byte&=0x0F;
  12. byte=byte+0x30;
  13. data_tx[1]=byte;
  14.  
  15. byte=word>>4;
  16. byte&=0x0F;
  17. byte=byte+0x30;
  18. data_tx[2]=byte;
  19.  
  20. byte=word;
  21. byte&=0x0F;
  22. if(byte<0x0A)
  23. {
  24. byte=byte+0x30;
  25. data_tx[3]=byte;
  26. }
  27. else
  28. {
  29. byte=byte+0x37;
  30. data_tx[3]=byte;
  31. }
  32.  
  33. for(uint8_t tx=0; tx<4; tx++) usart0_write(data_tx[tx]);
  34. }

Здесь мы передаем символы из массива юникода символов смс text_ucs2[i] полученных ранее из функции text_unicode(text_sms[i]);

Далее просто преобразуем их в строку data_tx[tx] и отправляем в порт усарт.

6) Теперь о всей программе расскажу.
Настраиваем наш модуль на скорость, у меня почему то работает только на 115200.

  1. //Функция at_ok() вернет 0 в случае правильной настройки модуля и 1
  2. //если не получилась и нужно попробовать ещё.
  3. while(at_ok())
  4. {
  5. at_ok(); //Шлем ОК
  6.  
  7. ATV0; //Отключаем словесный формат ответа.
  8. //Теперь вместо ОК будет проходить 0(в ASCII конечно)
  9. _delay_ms(1000); // Ждем.
  10. ATE0; //Отключаем эхо чтобы сами себя не слушали
  11.  
  12. _delay_ms(1000); //И опять ждем.
  13. }
  14.  
  15.  
  16. CGMF1; //Устанавливаем режим PDU.
  17.  
  18. while(ok()); //Ждем ОК.
  19.  
  20. CSCS_UCS2; //Посылаем команду AT+CSCS="UCS2"
  21. //Она настраивает на юникод.
  22. while(ok()); //Ждем ОК.
  23.  
  24. //Далее можно все загнать в отдельную функцию
  25.  
  26. //Здесь первый параметр номер телефона, а второй текст смски.//
  27. //И номеров и текстов смс четыре начиная с нуля.//
  28.  
  29. int sms_tx(uint16_t num_tel, uint16_t num_sms)
  30. {
  31. usart0_clear_tx_buffer(); //Clear Tx buffer.
  32. usart0_clear_rx_buffer(); //Clear Rx buffer.
  33.  
  34. CMGF1; //Макрос уставливает режим PDU.
  35.  
  36. uint8_t status=1;
  37. while(status)
  38. {
  39. status=ok();
  40. if(status=='4') return 0;
  41. }
  42.  
  43. SMS; //Говорим модему, что хотим послать SMS.
  44.  
  45. _delay_ms(100);
  46. if(num_tel==0) NUMBER0_UCS2; //На номер #0.
  47. if(num_tel==1) NUMBER1_UCS2; //На номер #1.
  48. if(num_tel==2) NUMBER2_UCS2; //На номер #2.
  49. if(num_tel==3) NUMBER3_UCS2; //На номер #3.
  50.  
  51. ENTER_SMS; //После этой команды вводим текст смски.
  52.  
  53. while(!(usart0_rx_len())); //Ждем приглашение от модуля.
  54. while(usart0_read()!='>'); //Как только прийдет ">" можно будет вводить текст смс.
  55.  
  56. usart0_clear_tx_buffer(); //Clear Tx buffer.
  57.  
  58. if(num_sms==0)
  59. {
  60. uint8_t len=0;
  61.  
  62. for(uint16_t i=ADDR_EEP_SMS0; i<(0x47+ADDR_EEP_SMS0); i++)
  63. {
  64. if(eeprom_read_byte(i)) len++;
  65. }
  66.  
  67. char text_sms[len];
  68. uint8_t ii=0;
  69. for(uint16_t i=ADDR_EEP_SMS0; i<(len+ADDR_EEP_SMS0); i++)
  70. {
  71. text_sms[ii]=eeprom_read_byte(i);
  72. ii++;
  73. }
  74.  
  75. uint16_t text_ucs2[len];
  76.  
  77. for(uint16_t i=0; i<len; i++) text_ucs2[i]=text_unicode(text_sms[i]);
  78. for(uint16_t i=0; i<len; i++) text_unicode_hex(text_ucs2[i]);
  79. }
  80.  
  81. if(num_sms==1)
  82. {
  83. uint8_t len=0;
  84.  
  85. for(uint16_t i=ADDR_EEP_SMS1; i<(0x47+ADDR_EEP_SMS1); i++)
  86. {
  87. if(eeprom_read_byte(i)) len++;
  88. }
  89.  
  90. char text_sms[len];
  91. uint8_t ii=0;
  92. for(uint16_t i=ADDR_EEP_SMS1; i<(len+ADDR_EEP_SMS1); i++)
  93. {
  94. text_sms[ii]=eeprom_read_byte(i);
  95. ii++;
  96. }
  97.  
  98. uint16_t text_ucs2[len];
  99.  
  100. for(uint16_t i=0; i<len; i++) text_ucs2[i]=text_unicode(text_sms[i]);
  101. for(uint16_t i=0; i<len; i++) text_unicode_hex(text_ucs2[i]);
  102.  
  103. }
  104.  
  105. if(num_sms==2)
  106. {
  107. uint8_t len=0;
  108.  
  109. for(uint16_t i=ADDR_EEP_SMS2; i<(0x47+ADDR_EEP_SMS2); i++)
  110. {
  111. if(eeprom_read_byte(i)) len++;
  112. }
  113.  
  114. char text_sms[len];
  115. uint8_t ii=0;
  116. for(uint16_t i=ADDR_EEP_SMS2; i<(len+ADDR_EEP_SMS2); i++)
  117. {
  118. text_sms[ii]=eeprom_read_byte(i);
  119. ii++;
  120. }
  121.  
  122. uint16_t text_ucs2[len];
  123.  
  124. for(uint16_t i=0; i<len; i++) text_ucs2[i]=text_unicode(text_sms[i]);
  125. for(uint16_t i=0; i<len; i++) text_unicode_hex(text_ucs2[i]);
  126. }
  127.  
  128. if(num_sms==3)
  129. {
  130. uint8_t len=0;
  131.  
  132. for(uint16_t i=ADDR_EEP_SMS3; i<(0x47+ADDR_EEP_SMS3); i++)
  133. {
  134. if(eeprom_read_byte(i)) len++;
  135. }
  136.  
  137. char text_sms[len];
  138. uint8_t ii=0;
  139. for(uint16_t i=ADDR_EEP_SMS3; i<(len+ADDR_EEP_SMS3); i++)
  140. {
  141. text_sms[ii]=eeprom_read_byte(i);
  142. ii++;
  143. }
  144.  
  145. uint16_t text_ucs2[len];
  146.  
  147. for(uint16_t i=0; i<len; i++) text_ucs2[i]=text_unicode(text_sms[i]);
  148. for(uint16_t i=0; i<len; i++) text_unicode_hex(text_ucs2[i]);
  149. }
  150.  
  151. END_SMS; //Макрос завершающий отправку смс.
  152.  
  153. while(ok());
  154. _delay_ms(100); //Ждем ОК.
  155.  
  156. return 1;
  157. }

В главном цикле можно написать проверку какого либо условия например для сигнализации проверить пин.

  1. while(1)
  2. {
  3. if(!(PIN_KEY & (1<<KEY0)))
  4. {
  5. //Здесь нужно отключить всякие таймеры и внешние прерывания, если они были до
  6. //этого. !!! Заметь не запретить прерывания а просто исключить те вещи которые их
  7. //могут создать.//
  8. sms_tx(0,0); //Шлем смс на первый номер.
  9. for(uint8_t p=0; p<5; p++) _delay_ms(1000);
  10. sms_tx(0,1); //Шлем смс на второй номер.
  11. for(uint8_t p=0; p<5; p++) _delay_ms(1000);
  12. sms_tx(0,2); //Шлем смс на третий номер.
  13. for(uint8_t p=0; p<5; p++) _delay_ms(1000);
  14. sms_tx(0,3); //Шлем смс на четвертый номер.
  15. for(uint8_t p=0; p<5; p++) _delay_ms(1000);
  16.  
  17. for(uint8_t p=0; p<10; p++) _delay_ms(1000);
  18.  
  19. usart0_clear_rx_buffer();
  20. usart0_clear_tx_buffer();
  21.  
  22. //А здесь можно опять включить таймеры, и др. вещи создающею прерывания//
  23. }

По поводу номеров, их можно подправить непосредственно в еепроме, если цифр в номере больше или меньше, то надо уже править эти макросы:

по смс

  1. #define NUMBER0_UCS2 for(uint16_t n=ADDR_EEP_NUM0; n<(12+ADDR_EEP_NUM0);
  2. n++) number0_unicode(n);
  3. #define NUMBER1_UCS2 for(uint16_t n=ADDR_EEP_NUM1; n<(12+ADDR_EEP_NUM1);
  4. n++) number1_unicode(n);
  5. #define NUMBER2_UCS2 for(uint16_t n=ADDR_EEP_NUM2; n<(12+ADDR_EEP_NUM2);
  6. n++) number2_unicode(n);
  7. #define NUMBER3_UCS2 for(uint16_t n=ADDR_EEP_NUM3; n<(12+ADDR_EEP_NUM3);
  8. n++) number3_unicode(n);

по дозвону

  1. #define NUMBER0_GSM for(uint16_t n=ADDR_EEP_NUM0; n<(12+ADDR_EEP_NUM0); n++) usart0_write(eeprom_read_byte(n));
  2. #define NUMBER1_GSM for(uint16_t n=ADDR_EEP_NUM1; n<(12+ADDR_EEP_NUM1); n++) usart0_write(eeprom_read_byte(n));
  3. #define NUMBER2_GSM for(uint16_t n=ADDR_EEP_NUM2; n<(12+ADDR_EEP_NUM2); n++) usart0_write(eeprom_read_byte(n));
  4. #define NUMBER3_GSM for(uint16_t n=ADDR_EEP_NUM3; n<(12+ADDR_EEP_NUM3); n++) usart0_write(eeprom_read_byte(n));

Цифра 12 это количество цифр в мобильном номере.

Адреса в тел. номеров и текстов смс написаны в этих дефайнах:

  1. #define ADDR_EEP_NUM0 0x0008
  2. #define ADDR_EEP_NUM1 0x0014
  3. #define ADDR_EEP_NUM2 0x0020
  4. #define ADDR_EEP_NUM3 0x002C
  5.  
  6. ///////////////////////////////////////////////
  7.  
  8. #define ADDR_EEP_SMS0 0x0038
  9. #define ADDR_EEP_SMS1 0x007F
  10. #define ADDR_EEP_SMS2 0x00C6
  11. #define ADDR_EEP_SMS3 0x010D

Их тоже нужно править, если вдруг сократишь или увеличишь количество цифр в номере, но там ничего сложного нет, будут вопросы пиши.

Текст смс должен быть строго 70 символов (0х010D-0х00C6=71 но последний символ не учитывается), недостающие символы необходимо заменить пробелами. Можно было заморочится на автоматическое вычисление размера смс, но адреса в еепроме будут плавать и дефайнами уже не обойтись, нужен будет алгоритм, а это мне не нужно проще пробелами замостить смс до 70 символа.

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

Скачать файл проекта