Побеждаем GPRS модуль от Амперки

image

Не успели мы победить шину CAN, как пришлось побеждать очередную железку, а именно, GPRS модуль. Такова она жизнь разработчика — всё время приходится кого-нибудь побеждать (тут должен стоять запрещённый смайл).

Для одного из заказных проектов мне понадобилось добавить возможность управления и получения телеметрии по GSM при помощи SMS. Посмотрел я на список доступных вариантов и остановился на GPRS Shield от Амперки. Почему нет? Прилично выглядит, выпускается известной компанией, имеет техподдержку, по цене не особо отличается от конкурентов и вообще производит очень приятное впечатление.

Но не тут-то было. О том квесте и невероятных курсах повышения квалификации которые мне пришлось пройти, интегрируя этот GPRS модуль с Arduino Mega Server вы можете узнать, нажав на кнопочку ниже.

Сам модуль


Как я уже сказал, сам модуль производит очень приятное впечатление своей аккуратностью исполнения и родством с известной компанией. Опять же, на сайте производителя есть примеры использования и родная библиотека. Поскольку модуль брендовый, есть надежда на адекватную поддержку в случае возникновения каких-то проблем с ним.

image

Забегая вперёд, скажу, что техподдержка исправно отвечала на мои вопросы в течение двух недель, искала баги в своей библиотеке и даже выпустила её новую версию по мотивам общения со мной. Но… заставлять работать модуль мне пришлось всё-таки самостоятельно.

Проект


Пара слов о проекте «GSM курятник» для которого потребовался GPRS модуль. Понадобилось разработать автоматизацию для управления курятником. Воистину в удивительное время мы живём, нынче курятнику требуется управление через веб-браузер, беспроводные Smart-сенсоры и телеметрия по GSM. Прямо стратегический объект какой-то, а не курятник.

image

Но раз надо — значит надо. Покупаем комплектующие, среди которых GPRS модуль и приступаем.

Ахиллесова пята


Теперь об особенностях работы модуля. В принципе, он работает. Он работает с примерами, представленными на сайте производителя и на его основе можно даже создать какой-нибудь несложный скетч. Но есть три «но».

Примечание. В проекте использовались две платы: Arduino Mega 2560 и Arduino Due и всё нижеизложенное относится именно к ним и только к ним.

Первое «но», хардверное. Модуль не работает с Arduino Due. Никак. Не помогла даже многодневная переписка с техподдержкой Амперки. Заставить работать GPRS Shield с Arduino Due не удалось ни мне, ни специалистам компании. И это очень обидно потому, что Due это отличный контроллер с большими возможностями и очень хотелось бы иметь возможность использовать его с GPRS Shield.

Второе «но», системное. Для работы модулю требуется библиотека SoftwareSerial. Когда я начинал своё общение с GPRS Shield и техподдержкой Амперки это было безальтернативным решением. После наших разбирательств была выпущена исправленная версия библиотеки с поддержкой работы по железному Serial, но… он так и не заработал ни на Due, ни на Mega. Что вообще труднообъяснимо — если модуль работает по SoftSerial, то что ему мешает работать по железному Serial?

Третье «но», концептуальное. Это ещё не всё. главная засада заключается в принципе работы библиотеки модуля. Она работает в закрытом режиме, то есть блокирует работу контроллера на время ожидания прихода SMS (а это 99% времени) и во время всех прочих операций модуля. Что это значит? Это значит, что на стандартной библиотеке от производителя вы не сможете построить ничего, кроме тестового скетча. Стратегический курятник вам не светит, как не светят ещё тысячи классных приложений вроде охранных систем, управления теплицами, управления умным домом по GSM и т. д. и т. п.

Подробнее о SoftwareSerial


На Arduino Due модуль не работает, поэтому будем говорить об Arduino Mega. Пикантность ситуации заключается в том, что имея на Mega три свободных хардверных порта Serial1, Serial2 и Seria3, мы вынуждены подключать и использовать библиотеку SoftwareSerial. Никакими мыслимыми и немыслимыми способами, включая перестановку джамперов на модуле и допрос с пристрастием техподдержки Амперки, не удалось заставить работать GPRS Shield с хардверными портами на Arduino Mega.

Ладно… Используем SoftwareSerial, но и тут нас ожидает засада. SoftwareSerial может работать на многих пинах Arduino Mega, но с модулем работает почему-то только на 62-м и 63-м пинах. Почему только на них? Загадка сия велика есть. Ответа на этот вопрос мы, судя по всему, не узнаем никогда.

Муки выбора


И вот, как обычно, мы приходим к пониманию, что стандартная библиотека непригодна для практического использования. Блокируя контроллер на время посылки о ожидания ответа GPRS модуля, библиотека блокирует и все остальные процессы. Контроллер просто выпадает из реальности на 99% времени, ожидая пока придёт запрос или ответ или не закончится таймаут.

Это ставит крест на любом применении GPRS Shield, кроме тестового: послал запрос включить розетку — она включилась, послал запрос выключить — она выключилась. Ни о какой работе веб-сервера, работе с датчиками, управлении актуаторами и прочем не может быть и речи, ничего из этого просто не будет работать.

Вопрос встал ребром — либо мы отказываемся от библиотеки производителя и пишем свой код управления GPRS Shield (легко сказать), либо отказываемся от самого модуля и ищем альтернативное решение. Но модуль то уже есть, поэтому было решено отказаться от библиотеки и написать свой код управления модулем.

Покорение Эвереста


Весь процесс создания кода, заменяющего стандартную библиотеку от Амперки я пропускаю, замечу только, что это было весьма и весьма непросто и потребовало значительной исследовательской работы, составления изощрённого алгоритма и немалых усилий по тестированию получившегося решения.

Для вас же всё будет просто — немного магии и готовый рабочий код модуля AMS для поддержки GPRS Shield готов.

image

Полный код модуля AMS, заменяющий стандартную библиотеку
/*
  Modul GPRS
  part of Arduino Mega Server project
*/

#ifdef GPRS_FEATURE

#include 

#define ALWAYS           1
#define GPRS_ON_PIN      2
#define GPRS_STATE_PIN   3
#define GPRS_RX_PIN     62
#define GPRS_TX_PIN     63
#define MASTER          "+7yyyxxxxxxx"
#define MESSAGE         "Hello"

#define CHECK_ERROR      0
#define CHECK_MARKER     1
#define CHECK_OK         2
#define NO_CHECK         3

#define MARKER_OK        "OK"
#define MARKER_NO_CHECK  "-"

// GPRS commands
#define DATA_TEMP1  "temp1"
#define DATA_CONT1  "cont1"
#define DATA_LEAK1  "leak1"
#define DATA_SMOKE1 "smoke1"
#define DATA_ALL    "all"
#define DATA_RELAY1 "relay1"
#define DATA_SERVO1 "servo1"
#define DATA_PERIOD "period"

#define CMD_RELAY1 "relay1="
#define CMD_SERVO1 "servo1="
#define CMD_PERIOD "period="

byte gprsPeriod = 24;

SoftwareSerial GPRS(GPRS_RX_PIN, GPRS_TX_PIN);

#define MAX_SOFT_SERIAL 128
char bufferGprs[MAX_SOFT_SERIAL];

int curBuf = 0;

#define MESSAGE_LENGTH  20
char message[MESSAGE_LENGTH];
char phone[16];
char datetime[24];


void gprsInit() {
  initStart("GPRS", true);
  GPRS.begin(9600);
  gprsOnOff();
  gprsStart();
  sendGprs("AT+CFUN=1", "OK");
  sendGprs("AT+CNMI=2,1", "OK");
  sendGprs("AT+CMGF=1", "OK");
  sendGprs("AT+CLIP=1", "OK");
  modulGprs = MODUL_ENABLE;
  initDone(true);
}

void clearBufferGprs() {
  for (int i = 0; i < curBuf; i++) {
    bufferGprs[i] = 0;
  }
}

void gprsStart() {
  while (ALWAYS) {
    delay(1000);
    curBuf = 0;
    GPRS.println("AT");
    if (GPRS.available() > 0) {
      while (GPRS.available() > 0) {
        bufferGprs[curBuf++] = GPRS.read();
      }
      bufferGprs[curBuf] = '\0';
      if (strcmp(bufferGprs, "AT\r\n\r\nOK\r\n") == 0) {
        timeStamp();
        Serial.println(" GPRS ON");
        break;
      } else {
          Serial.print(".");
        }
    }
    clearBufferGprs();
  }
} // gprsStart()

void gprsOnOff() {
  pinMode(GPRS_ON_PIN, OUTPUT);
  if (digitalRead(GPRS_STATE_PIN) != HIGH) {
    digitalWrite(GPRS_ON_PIN, HIGH);
    delay(3000);
  }
  digitalWrite(GPRS_ON_PIN, LOW);
}

bool clearSoftwareSerial() {
  if (GPRS.available() > 0) {
    while (GPRS.available() > 0) {
      char c = GPRS.read();
    }
  }
}

#define DELAY_GPRS_COMMAND 2000

byte sendGprs(String command, char marker[]) {
  unsigned long timer = millis();
  bool success = false;
  
  clearSoftwareSerial();
  clearBufferGprs();
  Serial.print(F("Send command: ")); //Serial.println(command);
  
  while (ALWAYS) {
    if (millis() - timer > DELAY_GPRS_COMMAND) {
      Serial.print(F("Send error: ")); Serial.println(command);
      //clearBufferGprs();
      return CHECK_ERROR;
    }
    curBuf = 0;
    GPRS.println(command);
    delay(280);
    if (GPRS.available() > 0) {
      while (GPRS.available() > 0) {
        char c = GPRS.read();
        if (curBuf > MAX_SOFT_SERIAL - 2) {break;}
        bufferGprs[curBuf++] = c;
        Serial.print(c);
      }
      Serial.println();
      bufferGprs[curBuf] = '\0';
      if (marker == MARKER_NO_CHECK) {
        Serial.print(F("Send no check, ")); Serial.println(millis() - timer);
        return NO_CHECK;
      }
      else if (StrContains(bufferGprs, marker)) {
        Serial.print(F("Send success, ")); Serial.println(millis() - timer);
        return CHECK_MARKER;
      }
      else if (StrContains(bufferGprs, MARKER_OK)) {
        Serial.print(F("Send success, ")); Serial.println(millis() - timer);
        return CHECK_OK;
      } else {
          Serial.println(".");
        }
    } // if (GPRS.available() > 0)
    delay(500);
  } // while (ALWAYS)
} // sendGprs( )

// +CMGR: "REC READ", "XXXXXXXXXXX", "", "16/10/01,10:00:00+12"
// SMS text

void parseSms(char *message, char *phone, char *datetime) {
  int i = 0;
  int j = 0;
  while (bufferGprs[i] != '\"') {i++;} i++;
  while (bufferGprs[i] != '\"') {i++;} i++;
  while (bufferGprs[i] != '\"') {i++;} i++;
  while (bufferGprs[i] != '\"') {phone[j++] = bufferGprs[i++];} phone[j] = '\0'; i++;
  while (bufferGprs[i] != '\"') {i++;} i++;
  while (bufferGprs[i] != '\"') {i++;} i++;
  while (bufferGprs[i] != '\"') {i++;} i++;
  j = 0;
  while (bufferGprs[i] != '\"') {datetime[j++] = bufferGprs[i++];} datetime[j] = '\0'; i++;
  while (bufferGprs[i] != '\n') {i++;} i++;
  j = 0;
  //Serial.print(F("strlen(bufferGprs): ")); Serial.println(strlen(bufferGprs));
  while (i < strlen(bufferGprs) - 1) {
    if (j > MESSAGE_LENGTH - 1) {break;}
    if ((byte)bufferGprs[i] == 13) {break;}
    message[j++] = bufferGprs[i++];
  }
  Serial.print(F("strlen(message1): ")); Serial.println(strlen(message));
  message[j] = '\0';
  Serial.print(F("strlen(message2): ")); Serial.println(strlen(message));

  for (int z = 0; z < strlen(message); z++) {
    Serial.print((byte)message[z]);
    Serial.print(F(" "));
  }
  Serial.println();
}

void deleteSms() {
  if (sendGprs("AT+CMGDA=\"DEL ALL\"", "OK")) {
    Serial.println(F("All SMS deleted"));
  } else {
      Serial.println(F("Error delete all SMS"));
    }
}

String stringSens(byte v)  {
  String s = "";
  switch (v) {
    case 0:
      s = (F("OFF"));
      break;
    case 1:
      s = (F("ON"));
      break;
    default: 
      s = (F("?"));
  }
  return s;
}

String stringLeak(byte v)  {
  String s = "";
  switch (v) {
    case 0:
      s = (F("LEAK!"));
      break;
    case 1:
      s = (F("OK"));
      break;
    default: 
      s = (F("?"));
  }
  return s;
}

String stringSmoke(byte v)  {
  String s = "";
  switch (v) {
    case 0:
      s = (F("OK"));
      break;
    case 1:
      s = (F("SMOKE!"));
      break;
    default: 
      s = (F("?"));
  }
  return s;
}

String mkTemp1()  {String s = DATA_TEMP1;  s += '='; s += String(lpTempTemp);    s += '\n'; return s;}
String mkCont1()  {String s = DATA_CONT1;  s += '='; s += stringSens(lpContCont1);   s += '\n'; return s;}
String mkLeak1()  {String s = DATA_LEAK1;  s += '='; s += stringLeak(lpLeakLeak1);   s += '\n'; return s;}
String mkSmoke1() {String s = DATA_SMOKE1; s += '='; s += stringSmoke(smokeSmoke);   s += '\n'; return s;}
String mkRelay1() {String s = DATA_RELAY1; s += '='; s += stringSens(relayRelay);   s += '\n'; return s;}
String mkServo1() {String s = DATA_SERVO1; s += '='; s += stringSens(servoState); s += '\n'; return s;}
String mkPeriod() {String s = DATA_PERIOD; s += '='; s += String(gprsPeriod);   s += '\n'; return s;}

String mkAll() {
  String s = "";
  s += mkTemp1();
  s += mkCont1();
  s += mkLeak1();
  s += mkSmoke1();
  s += mkRelay1();
  s += mkServo1();
  s += mkPeriod();
  return s;
}

void gprsSetRelay(byte v) {
  switch (v) {
    case 0:
      if (relayRelay) {
        relayRelay = STATE_OFF;
      }
      break;
    case 1:
      if (!relayRelay) {
        relayRelay = STATE_ON;
      }
      break;

  }
}

void gprsSetServo(byte v) {
  switch (v) {
    case 0:
      servoState = STATE_OFF;
      break;
    case 1:
      servoState = STATE_ON;
      break;

  }
}

void gprsAnswer() {
  String s = "";
  String mess = String(message);
  String data = "";
  if      (mess == DATA_TEMP1)  {s += mkTemp1();}
  else if (mess == DATA_CONT1)  {s += mkCont1();}
  else if (mess == DATA_LEAK1)  {s += mkLeak1();}
  else if (mess == DATA_SMOKE1) {s += mkSmoke1();}
  else if (mess == DATA_RELAY1) {s += mkRelay1();}
  else if (mess == DATA_SERVO1) {s += mkServo1();}
  else if (mess == DATA_PERIOD) {s += mkPeriod();}
  else if (mess == DATA_ALL)    {s += mkAll();}
  else if (mess.indexOf(F("=")) >= 0) {
    byte p = mess.indexOf(F("="));
    if      (mess.indexOf(CMD_RELAY1) >= 0) {data = mess.substring(p + 1); gprsSetRelay(data.toInt()); s += DATA_RELAY1; s += '='; s += stringSens(relayRelay);}
    else if (mess.indexOf(CMD_SERVO1) >= 0) {data = mess.substring(p + 1); gprsSetServo(data.toInt()); s += DATA_SERVO1; s += '='; s += stringSens(servoState);}
    else if (mess.indexOf(CMD_PERIOD) >= 0) {data = mess.substring(p + 1); gprsPeriod = data.toInt();  s += DATA_PERIOD; s += '='; s += String(gprsPeriod);}
  } else {
      Serial.println(F("Not command!"));
    }

  //Serial.print(F("mess: ")); Serial.println(mess);
  //Serial.print(F("answ: ")); Serial.println(s);

  if (s == "") {s = "Error";}

  Serial.println(F("Send answer... "));
  if (sendSms(MASTER, s)) {
    Serial.println(F("success"));
  } else {
      Serial.println(F("error"));
    }
}

void readSms() {
  byte result = sendGprs("AT+CMGR=1,1", "+CMGR:");
  if (result == CHECK_MARKER) {
    parseSms(message, phone, datetime);
    Serial.print(F("Number:   ")); Serial.println(phone);
    Serial.print(F("Datetime: ")); Serial.println(datetime);
    Serial.print(F("Message:  ")); Serial.println(message);
    deleteSms();
    if (String(phone) == String(MASTER)) {
      Serial.println(F("Message from MASTER!"));
      gprsAnswer();
    }
  }
  else if (result == CHECK_OK) {
    Serial.println(F("No SMS"));
  } else {
      Serial.println(F("Error read SMS"));
    }
}

bool sendSms(char *number, String data) {
  String numstr = "AT+CMGS=\"" + String(number) + "\"";
  String messtr = data + String((char)26);
  if (sendGprs(numstr, ">")) {
    if (sendGprs(messtr, MARKER_NO_CHECK)) {
      return true;
    }
  }
  return false;
}

void sendPeriod() {
  Serial.println(F("Send period SMS... "));
  if (sendSms(MASTER, mkAll())) {
    Serial.println(F("success"));
  } else {
      Serial.println(F("error"));
    }
}

void gprsWorks() {
  if (cycle20s) {
    readSms();
  }

  switch (gprsPeriod) {
    case 1:  if (cycle1h)  {sendPeriod();} break;
    case 6:  if (cycle6h)  {sendPeriod();} break;
    case 12: if (cycle12h) {sendPeriod();} break;
    default: if (cycle24h) {sendPeriod();} break;
  }
}

#endif // GPRS_FEATURE

Модуль разрабатывался и тестировался на AMS версии 0.16 для Arduino Mega. Тестирование показало абсолютно стабильную и надёжную работу GPRS Shield под управлением этого модуля.

Подключение к AMS модуля поддержки GPRS Shield


Для того, чтобы подключить модуль GPRS Shield к Arduino Mega Server, нужно произвести несколько несложных действий. Сначала нужно добавить в главный файл AMS следующие строки

#define GPRS_FEATURE


и

byte modulGprs = MODUL_NOT_COMPILLED;


в соответствующие секции. Туда же нужно добавить тестовые переменные, которые в вашем реальном проекте будут отвечать за контролируемые параметры (температура, состояние контакта и т. д.).

float lpTempTemp;
byte lpContCont1;
byte lpLeakLeak1;
byte smokeSmoke;
byte relayRelay;
byte servoState;

Пояснения по поддерживаемым функциям


Модуль содержит базовый набор запросов и команд, но вы можете заменить эти команды любыми другими и/или расширить базовый набор командами и запросами нужными вам.

Запросы


Запросы посылаются посредством SMS, система присылает ответы (тоже по SMS), содержащие информацию о запрашиваемом параметре. Очень просто: посылаем с телефона запрос «temp1», система в ответ присылает текущее значение температуры temp1=20.50.

temp1 — запрос температуры
cont1 — запрос состояния контакта
leak1 — запрос состояния датчика протечки
smoke1 — запрос состояния датчика задымления
relay1 — запрос состояния реле (ключа)
servo1 — запрос состояния сервопривода кормушки
period — запрос периода автоматических посылок телеметрии
all — запрос всех параметров системы

В ответ на запрос «all» система присылает список всех параметров. В случае посылки незарегистрированной команды, система пришлёт ответ «Error». Если контролируемые параметры выходят за пределы нормы, система может сама прислать тревожные сообщения на смартфон оператора.

Для того, чтобы управлять системой мог только зарегистрированный оператор, в системе есть защита от несанкционированного доступа — она реагирует только на команды с определённого номера (номеров) телефона.

Команды


Команды посылаются посредством SMS, приняв команду, система меняет своё состояние и присылает ответы с информацией о новом состоянии своих актуаторов или настроек.

relay1= — команда управления реле (ключом)
servo1= — команда управления сервоприводом кормушки
period= — команда изменения периода автопосылки телеметрии

Пример. Команда relay1=1, ответ relay1=ON. Команда relay1=0, ответ relay1=OFF.

Пояснения по работе кода


Инициализация модуля GPRS. Стандартная инициализация модуля AMS, запуск SoftwareSerial, и немного магии GSM AT команд. Цель этого блока — привести в чувство модуль GPRS и вернуть его из нирваны, если он к этому моменту в неё ушёл по каким-то независящим от нас причинам.

void gprsInit() {
  initStart("GPRS", true);
  GPRS.begin(9600);
  gprsOnOff();
  gprsStart();
  sendGprs("AT+CFUN=1", "OK");
  sendGprs("AT+CNMI=2,1", "OK");
  sendGprs("AT+CMGF=1", "OK");
  sendGprs("AT+CLIP=1", "OK");
  modulGprs = MODUL_ENABLE;
  initDone(true);
}


Главная рабочая функция модуля. Каждые 20 секунд Arduino Mega Server проверяет наличие пришедших SMS и, в случае прихода каких-либо запросов или команд, выполняет их. В результате средняя задержка выполнения команды составляет 10 секунд (без учёта задержки передачи SMS оператором связи). Этот интервал можно настраивать, 20 секунд выбраны как компромисс между скоростью реакции на SMS команды и загрузкой системы.

Эти 20 секунд AMS может делать всё, что ему нужно, а приходящее в этот промежуток времени SMS не теряется. При использовании стандартной библиотеки система ничего не может делать кроме ожидания прихода SMS, иначе она их просто теряет (и контроллер в это время тоже ничего не может делать).

Тут же присутствует код посылки телеметрии с заданным интервалом в 1 час, 6 часов, 12 часов или 24 часа. Этот интервал можно изменить путём посылки по SMS соответствующей команды.

void gprsWorks() {
  if (cycle20s) {
    readSms();
  }

  switch (gprsPeriod) {
    case 1:  if (cycle1h)  {sendPeriod();} break;
    case 6:  if (cycle6h)  {sendPeriod();} break;
    case 12: if (cycle12h) {sendPeriod();} break;
    default: if (cycle24h) {sendPeriod();} break;
  }
}


Взаимодействие GPRS модуля и контроллера производится при помощи специальных AT команд и нижеследующие функции формируют команды GPRS модулю в понятных для него кодах.

Функция посылки SMS. В параметрах функции передаётся телефонный номер назначения и сама команда или запрос в текстовом виде.

bool sendSms(char *number, String data) {
  String numstr = "AT+CMGS=\"" + String(number) + "\"";
  String messtr = data + String((char)26);
  if (sendGprs(numstr, ">")) {
    if (sendGprs(messtr, MARKER_NO_CHECK)) {
      return true;
    }
  }
  return false;
}


Функция чтения SMS. Принятые значения помещаются в переменные message, phone и datetime, которые содержат, соответственно, пришедшую команду, номер телефона и время посылки.

void readSms() {
  byte result = sendGprs("AT+CMGR=1,1", "+CMGR:");
  if (result == CHECK_MARKER) {
    parseSms(message, phone, datetime);
    Serial.print(F("Number:   ")); Serial.println(phone);
    Serial.print(F("Datetime: ")); Serial.println(datetime);
    Serial.print(F("Message:  ")); Serial.println(message);
    deleteSms();
    if (String(phone) == String(MASTER)) {
      Serial.println(F("Message from MASTER!"));
      gprsAnswer();
    }
  }
  else if (result == CHECK_OK) {
    Serial.println(F("No SMS"));
  } else {
      Serial.println(F("Error read SMS"));
    }
}


Функция формирования ответа на SMS запросы и команды. Эта функция специфична для каждого проекта и вы в своих проектах можете изменить её в соответствии с логикой работы вашей системы.

void gprsAnswer() {
  String s = "";
  String mess = String(message);
  String data = "";
  if      (mess == DATA_TEMP1)  {s += mkTemp1();}
  else if (mess == DATA_CONT1)  {s += mkCont1();}
  else if (mess == DATA_LEAK1)  {s += mkLeak1();}
  else if (mess == DATA_SMOKE1) {s += mkSmoke1();}
  else if (mess == DATA_RELAY1) {s += mkRelay1();}
  else if (mess == DATA_SERVO1) {s += mkServo1();}
  else if (mess == DATA_PERIOD) {s += mkPeriod();}
  else if (mess == DATA_ALL)    {s += mkAll();}
  else if (mess.indexOf(F("=")) >= 0) {
    byte p = mess.indexOf(F("="));
    if      (mess.indexOf(CMD_RELAY1) >= 0) {data = mess.substring(p + 1); gprsSetRelay(data.toInt()); s += DATA_RELAY1; s += '='; s += stringSens(relayRelay);}
    else if (mess.indexOf(CMD_SERVO1) >= 0) {data = mess.substring(p + 1); gprsSetServo(data.toInt()); s += DATA_SERVO1; s += '='; s += stringSens(servoState);}
    else if (mess.indexOf(CMD_PERIOD) >= 0) {data = mess.substring(p + 1); gprsPeriod = data.toInt();  s += DATA_PERIOD; s += '='; s += String(gprsPeriod);}
  } else {
      Serial.println(F("Not command!"));
    }

  //Serial.print(F("mess: ")); Serial.println(mess);
  //Serial.print(F("answ: ")); Serial.println(s);

  if (s == "") {s = "Error";}

  Serial.println(F("Send answer... "));
  if (sendSms(MASTER, s)) {
    Serial.println(F("success"));
  } else {
      Serial.println(F("error"));
    }
}


Функции формирования ответов. Это функции которые формируют ответы, которые система посылает по SMS на смартфон пользователя в ответ на запрос или по своему усмотрению, в соответствии с заложенной программой.

String stringSens(byte v) 
String stringLeak(byte v)
String stringSmoke(byte v)

String mkTemp1()
String mkCont1()
String mkLeak1()
String mkSmoke1()
String mkRelay1()
String mkServo1()
String mkPeriod()
String mkAll()


И функции выполнения команд. Это функции, которые выполняют пришедшие по SMS команды и меняют состояние системы, т. е. изменяют её настройки или включают или отключают актуаторы.

void gprsSetRelay(byte v)
void gprsSetServo(byte v)


Остальные функции являются чисто техническими, делающими всю черновую работу по обслуживанию взаимодействия между GPRS Shield и микроконтроллером.

Ещё немного о SoftwareSerial


Всего вышеприведённого недостаточно, чтобы GPRS Shield заработал в прозрачном режиме и перестал блокировать остальные процессы, работающие на микроконтроллере. Нужно ещё модифицировать код библиотеки SoftwareSerial. Дело в том, что её стандартного буфера в 64 байта недостаточно для переваривания большинства AT GSM команд и поддержания динамического взаимодействия между модулем и контроллером.

Нужно увеличить этот буфер как минимум до 128 байт и только после этого магия заработает и GPRS Shield начнёт нормально работать в прозрачном режиме. Делается это в файле SoftwareSerial.h. Строку

#define _SS_MAX_RX_BUFF 64 // RX buffer size


нужно поменять на

#define _SS_MAX_RX_BUFF 128 // RX buffer size


Заключение


Вы видите обмен командами оператора и ответы системы на экране смартфона. И при этом система выполняет ещё десятки процессов в псевдо-многозадачном режиме. А GPRS Shield стал занимать менее 1% процессорного времени, вместо 99% как раньше, освободив остальное время для веб-сервера и прочих задач по управлению курятником.

image

Вот и всё, GPRS Shield мы интегрировали в AMS, преодолели все препятствия, заставили его работать в прозрачном режиме, не блокируя остальные процессы вроде веб-сервера, работы беспроводных сенсоров и актуаторов и это открывает заманчивые перспективы по построению огромного количества систем с GSM SMS управлением и контролем на основе AMS — теплиц, охранных и отопительных систем, различных вариантов умного дома и т. д. и т. п. практически до бесконечности.

© Geektimes