Подключение модуля gsm/gprs a6 (расширенная версия) к Arduino UNO R3

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Homo Faber пишет:

где я опять накосячил?

практически везде.

write и read команды оперируют одним байтом, зачем в init 52 и 55 строка вообще не понятно

зачем ставить скорость 115200 а потом 9600, что  за извращение

какими командами и как инициализироват модем А6 уже писалось

светодиод загорается но не видно где он должен гаснуть

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

http://codius.ru/articles/GSM_%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C_SIM800L_%D1%87%D0%B0%D1%81%D1%82%D1%8C_1

 

Homo Faber
Offline
Зарегистрирован: 25.05.2019

Прочёл, проникся, исправил. Вот новый код и ответ на него.

#include <SoftwareSerial.h>

// 8 - RX Arduino (TX GSM_A6), 9 - TX Arduino (RX GSM_A6)
SoftwareSerial GSM_A6(8, 9);// 8 - RX Arduino (TX GSM_A6), 9 - TX Arduino (RX GSM_A6)

unsigned long current_millis=millis();
int ch = 0;
int LedG = 4;                                 // Пин для зелёного светодиода
int LedR = 7;                                 // Пин для красного светодиода
String val = "";
byte Flag=false;

void setup()
{
  pinMode(LedG, OUTPUT);
  digitalWrite(LedG, LOW);                    // Зелёный светодиод ВЫКЛ
  pinMode(LedR, OUTPUT);
  digitalWrite(LedR, LOW);                    // Красный светодиод ВЫКЛ
  Serial.begin(9600);                         // Скорость обмена данными с компьютером
  Serial.println("Start!");
  GSM_A6.println("AT+IPR=9600");
  GSM_A6.begin(9600);                         // Скорость обмена данными с модемом 9600
  initModem();
}

void loop() {
  if (GSM_A6.available())                     // Ожидаем прихода данных (ответа) от модема...
     {
      Serial.write(GSM_A6.read());            // ...и выводим их в Serial
     }
  if (Serial.available())                     // Ожидаем команды по Serial...
     {
      GSM_A6.write(Serial.read());          // ...и отправляем полученную команду модему
     }
}

void initModem()
{
  while ((millis() - current_millis) <= 5000UL)
        {
         GSM_A6.println("AT");                // Посылаем команду АТ
         delay(1000);
// Контроль ответа ОК
         val="";
         while (GSM_A6.available())
               {
                ch = GSM_A6.read();
                val += char(ch);              // Сохраняем входную строку в переменную val
                delay(10);
                if (val.indexOf("OK"))
                   {
                    Serial.println("LedG");
                    digitalWrite(LedG, HIGH); // Зелёный светодиод ВКЛ
                    Flag=true;
                    break;
                   }
               }
         if (Flag) break;      
        }
  GSM_A6.println("AT+IPR?");                  // Посылаем запрос о скорости соединения
// Контроль скорости 9600
  val="";
  while (GSM_A6.available())
        {
         ch = GSM_A6.read();
         val += char(ch);                     // Сохраняем входную строку в переменную val
         delay(10);
         if (val.indexOf("9600"))
            {
             Serial.println("LedR");
             digitalWrite(LedR, HIGH);        // Красный светодиод ВКЛ
             break;
            }
        }
  delay(1000);
  GSM_A6.println("ATI");                      // Посылаем запрос о версии модуля
}

А вот ответ.

Start!
LedG
LedR
 
 
OK
AT+IPR?
 
+IPR: 9600
 
OK
ATI
 
Ai Thinker Co.LTD
A6 
V03.03.20161229019H03
 
OK
 
Чего я не могу понять, так это почему мои сообщения "LedG" и "LedR" (и соответсвенно включаются светодиоды) выводятся раньше ответа модема? И почему на мой запрос "АТ" всё также приходит ответ в виде набора чисел? Ведь эту часть я срисовал с http://codius.ru/articles/GSM_модуль_SIM800L_часть_1.
До SMS надеюсь когда-нибудь таки дойду...
Homo Faber
Offline
Зарегистрирован: 25.05.2019

Честно говоря я что-то не понял. После очередного запуска модем стал отвечать эхом, то есть что я посылаю, то и получаю в ответ. Но только на команды введённые через монитор порта.

Примерно так.

Start!
LedG
LedR
 
 
OK
AT+IPR?
 
+IPR: 9600
 
OK
ATI
 
Ai Thinker Co.LTD
A6 
V03.03.20161229019H03
 
OK
AT
AT+IPR?
ATI
 
Дал команды сответственно "AT", "AT+IPR?" и "ATI" и получил из же в ответ. На Arduino явственно перемигиваются светодиоды TX и RX в момент передачи команды иполучения ответа. Что бы это значило?
Мой чайник скоро закипит, но не от удачной работы, а от непонимания...
andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Про автоответ это банально - есть команда модема которая отключает автоответ - нагуглите легко.
По поводу остального - у вас логика программы страдает, вы выводите сообщения до того как вывод ответа, да и вообще все криво.
У вас как вообще с программированием?
Т е вам надо взять в толк что работает все последовательно, необходимо в loop разделять режимы работы программы и в зависимости от это этого выводить сообщения. Куча примеров на форуме....

Homo Faber
Offline
Зарегистрирован: 25.05.2019

"АТЕ0" пробовал - не помогло, просто никакого ответа. Программированием занимаюсь профессионально, около 30 лет. На логику никто не жаловался. С языком СОСИ++ связался впервые. Настолько идиотские конструкции, что поневоле принимаешь за правду то, что он был придуман как пример языка с невозможными конструкциями, в качестве первоапрельской шутки студентами МТИ. Почему-то очень понравился профессору. С этого момента и началось шествие С по умам программистов.
По поводу последовательности команд. Если я подав команду "АТ" дожидаюсь ответа модема "ОК" и ТОЛЬКО ПОСЛЕ ЭТОГО вывожу "LedG" и включаю зелёный светодиод, затем подаю команду "AT+IPR?", дожидаюсь ответа "+IPR: 9600" и ТОЛЬКО ПОСЛЕ ЭТОГО вывожу "LedR" и включаю красный светодиод. В чём отсутствие логики?
Кину ещё один камень в огород С. Хреново работать со строками. Нет элементарных функций наподобие LEFT(), RIGHT() для выделения определённого количества символов из строки. Пытался подключить "STRING.H", "CSTRING.H", меняне поняли ;). Получилответ,что библиотеки не подходят. Можете что-либо посоветовать?

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Homo Faber пишет:

................................ Программированием занимаюсь профессионально, около 30 лет. На логику никто не жаловался. .......................................................
Кину ещё один камень в огород С. ..........................................

че то не вяжеться, тут частенько приходят супер программисты, а примитивного сделать не могут :(

ЗЫ. Извините, но я уйду с этой темы. Еслиб у Вас было желание разобраться - давно бы уже кучу примеров разобрали.

P.P.S. Не хвалюсь - просто констатация факта, ни капли не программист, работаю поверхностностно с: Ассемблер,C,Pascal,PHP,C++ ну ни разу не было желания поругаться на язык, это всего лишь инструмент, который надо учить.

Homo Faber
Offline
Зарегистрирован: 25.05.2019

И всё-таки я его добил. И он подчинился...

Вот скетч, там много всяких, возможно ненужных наворотов, но, по-моему, важен принцип - работает устойчиво.

#include <SoftwareSerial.h>                                         // Библиотека програмной реализации обмена по UART-протоколу
SoftwareSerial GSM_A6(8, 9);                                        // 8 - RX, 9 - TX

int Pins[4] = {2, 4, 7, 12};                                        // Пины с подключенными светодиодами
int Zvuk=3;                                                         // Пин зуммера
bool CorrFlg;                                                       // Флаг корректного завершения
bool CorrTlf;                                                       // Признака вхождения номера телефона в БЕЛЫЙ СПИСОК
String Mdm_Rsp = "";                                                // Переменная для хранения ответа модуля
long Upd_Lst = millis();                                            // Время последнего обновления
long Upd_Prd = 60000;                                               // Проверять каждую минуту
int Cnt=0;                                                          // Счётчик на все случаи жизни
int LED_Ctrl=0;                                                     // Контроль включения светодиодов

// БЕЛЫЙ СПИСОК телефонов в виде массива строк, с ОБЯЗАТЕЛЬНЫМ(!) "End" в конце списка
char* Ph_Nums[] = {"+7922*******", "+7919*******", "+7919*******", "+7982*******", "End"};

void setup()
{
  delay(10000);                                                     // Задержка 10 секунд перед запуском программы
  for (Cnt = 0; Cnt < 4; Cnt++)
      {
       pinMode(Pins[Cnt], OUTPUT);                                  // Настраиваем пины в OUTPUT
      }
  GSM_A6.begin(9600);                                               // Скорость обмена данными с модемом
  Send_AT_Cmd("AT", true);                                          // Отправка AT для настройки скорости обмена данными
  Send_AT_Cmd("AT+CMGD=1,4", true);                                 // Удаляем все сообщения, чтобы не забивали SIM-карту
  Send_AT_Cmd("AT+CLIP=1", true);                                   // Включаем АОН
  Send_AT_Cmd("ATE0;&W", true);                                     // Выключаем эхо-режим и сразу сохраняем значение (AT&W)!
  Send_AT_Cmd("AT+CMGF=1;&W", true);                                // Включаем текстовый режима SMS (Text mode) и сразу сохраняем значение (AT&W)!
  String M_R=Send_AT_Cmd("AT+IPR?", true);                          // Запрос скорости обмена с модемом
  if (M_R.indexOf("9600"))                                          // Контроль скорости обмена с модемом
     {
      digitalWrite(12, HIGH);                                       // Индикация нормальной связи с модемом
     }
  Send_AT_Cmd("AT+CPMS=\"SM\",\"SM\",\"SM\"", true);                // Команда распределения памяти (хранить SMS сообщения в памяти SIM-карты)
  Upd_Lst = millis();                                               // Обнуляем таймер
}

// Основная программа
void loop()
{
  if (GSM_A6.available())                                           // Если у модема есть что сказать
     {
      Mdm_Rsp = Wait_Rsp();                                         // Получаем ответ от модема для анализа
      Mdm_Rsp.trim();                                               // Убираем лишние пробелы в начале и конце
      if (Mdm_Rsp.startsWith("+CIEV:"))                             // Пришло сообщение о получении SMS
         {
          RazborSMS(Mdm_Rsp);                                       // Разобрать SMS на элементы
         }
     }
}

// Подпрограмма разбора полученной SMS с определением номера телефона и команды
void RazborSMS(String SMS_Msg)
{
  String SMS_Msgheader  = "";                                       // Объявление строковой переменной заголовка
  String SMS_Cmd    = "";                                           // Объявление строковой переменной команды
  int index = SMS_Msg.indexOf("+7");                                // Находим первое вхождение "+7", получаем индекс
  String Ph_Num = SMS_Msg.substring(index, index+12);               // Получаем номер телефона
  String SMS_MsgToSend = "";                                        // Объявление строковой переменной SMS
  Ph_Num.trim();                                                    // Убираем пробелы в начале/конце

// Проверка номера телефона на вхождение в БЕЛЫЙ СПИСОК
  CorrTlf=false;                                                    // Сброс признака вхождения номера телефона в БЕЛЫЙ СПИСОК
  Cnt=0;                                                            // Обнуление счётчика
  while (Ph_Nums[Cnt]!="End")                                       // Перебор номеров телефонов с целью определения вхождения в БЕЛЫЙ СПИСОК
        {
         if (Ph_Num.compareTo(Ph_Nums[Cnt])==0)                     // Номер телефона совпал с имеющимся в БЕЛОМ СПИСКЕ
            {
              CorrTlf=true;                                         // Установка признака вхождения номера телефона в БЕЛЫЙ СПИСОК
              break;                                                // Выход из цикла при нахождении номера
            }
         Cnt++;                                                     // Приращение счётчика
        }
  if (CorrTlf)
     {
// Номер телефона входит в БЕЛЫЙ СПИСОК
      SMS_Msg = SMS_Msg.substring(SMS_Msg.indexOf("+CMT: "));       // Определяем начало строки заголовка
      SMS_Msgheader = SMS_Msg.substring(0, SMS_Msg.indexOf("\r"));  // Получаем заголовок

      SMS_Cmd = SMS_Msg.substring(SMS_Msgheader.length() + 2);      // Выделяем строку сообщения, включая "ОК"
      SMS_Cmd.toUpperCase();                                        // Переводим сообщение в верхний регистр
      SMS_Cmd = SMS_Cmd.substring(0, SMS_Cmd.lastIndexOf("OK"));    // Выделяем собственно команду
      SMS_Cmd.trim();                                               // Убираем лишние пробелы в начале и конце

      if (SMS_Cmd.length() == 2)                                    // Длина команды = 2 символам (корректно)
         {
          int LED_Ind = ((String)SMS_Cmd[0]).toInt();               // Получаем первую цифру команды - адрес устройства (1-3)
          int LED_St = ((String)SMS_Cmd[1]).toInt();                // Получаем вторую цифру команды - состояние (0 - выкл, 1 - вкл)
          if (LED_Ind >= 1 && LED_Ind <= 3 && (LED_St == 0 or LED_St == 1))
// Если адрес и состояние входят в допустимые границы
             {
              if (digitalRead(Pins[LED_Ind - 1])!=LED_St)           // Состояние светодиода должно изменится
                 {
// Если все нормально, исполняем команду
                  digitalWrite(Pins[LED_Ind - 1], LED_St);          // Исполнение команды
                  tone(Zvuk, 500*LED_Ind, 500);
                  if (digitalRead(Pins[LED_Ind - 1])==LED_St)       // Контроль корректности исполнения команды
                     {
// Формирование сообщения об ИСПОЛНЕНИИ(!) команды
                      SMS_MsgToSend = "LED:" + (String)LED_Ind + " set to " + (LED_St == 0 ? "OFF" : "ON")+". Uspeshno :-)";
                     }
                  else
                     {
// Формирование сообщения о НЕИСПОЛНЕНИИ(!) команды
                      SMS_MsgToSend = "LED:" + (String)LED_Ind + ". set to " + (LED_St == 0 ? "OFF" : "ON")+". Ne vypolneno :-(";
                     }
                 }
              else                                                  // Состояние светодиода НЕ должно изменится
                 {
// Формирование сообщения о НЕИЗМЕНЕНИИ состояния светодиода
                  SMS_MsgToSend = "LED:" + (String)LED_Ind + ". Sostoyanie ne izmenilos :-|";
                 }
             }
          else
             {
// Формирование сообщения о НЕВОЗМОЖНОСТИ выполнить команду
              SMS_MsgToSend = "Mission: Impossible :-(";
             }
         }
      else
         {
// Формирование сообщения о НЕКОРРЕКТНОСТИ команды
          SMS_MsgToSend = "Nekorrektnaya komanda :-(";
         }
     }
  else
// Номер телефона не входит в БЕЛЫЙ СПИСОК
     {
// Формирование сообщения о невхождении телефона в БЕЛЫЙ СПИСОК
      SMS_MsgToSend = "Izvinite, telefon ne opoznan! :-("; 
     }
  Send_SMS(Ph_Num, SMS_MsgToSend);                                  // SMS об РЕЗУЛЬТАТЕ команды
  Send_AT_Cmd("AT+CMGD=1,4", true);                                 // Удаляем все сообщения, чтобы не забивали SIM-карту
}

// Подпрограмма отправки SMS
void Send_SMS(String SMS_Num, String Ansver)
{
  Send_AT_Cmd("AT+CMGS=\"" + SMS_Num + "\"", true);                 // Передаём модему номер телефона и...
  Send_AT_Cmd(Ansver + "\r\n" + (String)((char)26), true);          // ...текст сообщения, после текста отправляем перенос строки и Ctrl+Z
}

// Подпрограмма отправки команд АТ модулю
String Send_AT_Cmd(String Cmd, bool Waiting)
{
  String M_Rsp = "";                                                // Переменная для хранения результата
  GSM_A6.println(Cmd);                                              // Отправляем команду модулю
  if (Waiting)                                                      // Если флаг ожидания TRUE
     {
      M_Rsp = Wait_Rsp();                                           // При необходимости ждем ответ
     }
  return M_Rsp;                                                     // Возвращаем данные, если таковые имеются
}

// Функция ожидания ответа и возврата полученного результата
String Wait_Rsp()
{
  String M_Rsp = "";                                                // Переменная для хранения результата
  long TimeOut = millis() + 10000;                                  // Переменная для отслеживания таймаута (10 секунд)
  while (!GSM_A6.available() && millis() < TimeOut)  {};            // Ждем ответа 10 секунд, если пришел ответ или наступил таймаут, то...
  if (GSM_A6.available())                                           // Если у модема есть что сказать
     {
      M_Rsp = GSM_A6.readString();                                  // Считываем и запоминаем данные модема
     }
  return M_Rsp;                                                     // Возвращаем данные, если таковые имеются
}

Номера телефонов скрыл, SIM карта в модеме от MOTIV. Для нормального запуска модема на 9600 НЕ ТРЕБУЕТСЯ многократной передачи AT+IPR=9600, один раз указал скорость обмена и он понял. Были некоторые сложности в начале пути, но в итоге всё заработало нормально. Скетч разработан ТОЛЬКО для проверки возможностей модема по приёму-передаче SMS.
Вот схема подключения. Расчита на работу под управлением скетча, но каждый волен переделать её и скетч под свои нужды. Плата модема показана условно, только выводы, из них,как видите, используется только 4.

Если у кого-то возникнут вопросы - милости прошу, отвечу по мере сил и умения.
Спасибо всем принявшим участие.

PS. А Си, действительно, трёхнутый на всю голову язык.
PPS. Гораздо лучший справочник по языку Си для Arduino находится тут: https://doc.arduino.ua/ru/prog/

sadman41
Offline
Зарегистрирован: 19.10.2016

Долбаните то же самое на ассемблере. После этого будете храмы ставить Cи на каждом перекрестке.

Homo Faber
Offline
Зарегистрирован: 25.05.2019

На заре своей трудовой деятельности, в году этак 1986-87, обслуживал СМ-4 и СМ-1420. После месячных курсов влёт писал тестовые программки для блоков... Сейчас уже, конечно, всё забылось.
А Си всё равно ипанутый язык, даже Fortran-77. смотрится логичнее...

sadman41
Offline
Зарегистрирован: 19.10.2016

Ну и что, эти ваши СМ-ки с модемами работали, Вы в асинхронном режиме перифирию программировали с лёту? 
(просто интересно)

 

Homo Faber
Offline
Зарегистрирован: 25.05.2019

Нет, они использовались локально, но сложность СМ-ки и Arduin-ки, сравнивать невозможно, ибо это слишком разные устройства. Программировал, естественно, не с лёту, нопрограммы в 150-200 строк писалисьза пару дней, так как за простой натягивали крепко. Зато в результате определась неисправная плата, а то и элемент на ней. Учился в Киеве, при заводе выпускавшем эти машины, учили добротно, коллектив преподователей (они же работники завода) знал своё дело туго. В то время, на каждую плату имелись принципиальные схемы, с контрольными точками, с эпюрами (осциллограммами), то есть искать неисправности приходилось не по наитию, а по приборам. А программы на Макроассемблере помогали быстрее найти неисправность и заменить мёртвый элемент. Если, конечно, это был не процессор. Тогда Цэшку и осциллограф в зубы и вперёд, на мины... Такое правда было всего один раз, тогда обошлось малой кровью, нашли непропай заводской... А вот с периферией таки да, раз в 2-3 месяца что-нибудь да помирало. Был курьёзный случай: утром включили машину, чере пару секунд из одного шкафа раздался грохот. Быстро выключили, открыли шкаф и охренели: из блока вентиляиторов сорвались лопасти, упали вовнутрь и частично перерубили плоский кабель межблочной связи (интерфейс). Заменили вентилятор и кабель за 20 минут и всё заработало.
Вы уж извините, ударился в воспоминания...