📜 ⬆️ ⬇️

We win GPRS module from Amperki

image
We did not have time to defeat the CAN bus , as we had to defeat another piece of hardware, namely, the GPRS module. This is the life of the developer - all the time someone has to win (there must be a forbidden smile).

For one of the custom projects I needed to add the ability to control and receive GSM telemetry via SMS. I looked at the list of available options and stopped at GPRS Shield from Amperki. Why not? It looks decent, is produced by a well-known company, has technical support, is not very different in price from its competitors and generally produces a very pleasant impression.

But it was not there. You can learn about that quest and incredible advanced training courses that I had to integrate this GPRS module with the Arduino Mega Server by clicking the button below.

Module itself


As I have already said, the module itself makes a very pleasant impression with its accuracy of execution and affinity with a well-known company. Again, on the manufacturer's website there are examples of use and the native library. Since the module is branded, there is hope for adequate support in case of any problems with it.
')
image

Looking ahead, I’ll say that tech support regularly answered my questions for two weeks, searched for bugs in my library and even released its new version based on the communication with me. But ... I had to make the module work on my own.

Project


A few words about the GSM Coop project for which the GPRS module was required. It took the development of automation to manage the coop. Truly, at an amazing time, we live, now the chicken coop requires control via a web browser, wireless smart sensors and telemetry via GSM. Directly strategic object of some kind, not a chicken coop.

image

But if it is necessary - it means it is necessary. We buy components, including the GPRS module, and proceed.

Achilles' heel


Now about the features of the module. Basically, it works. He works with examples presented on the manufacturer's website and on its basis one can even create some simple sketch. But there are three "but."

Note. The project used two boards: the Arduino Mega 2560 and the Arduino Due, and all of the following applies to them and only to them.

The first "but" hardware. Module does not work with Arduino Due. No Not even the multi-day correspondence with Amperka's technical support did not help. Making me work with GPRS Shield with Arduino Due failed neither to me nor to the company's specialists. And this is very disappointing because Due is an excellent controller with great capabilities and would love to be able to use it with GPRS Shield.

The second "but" system. The module requires the SoftwareSerial library to work. When I started my communication with GPRS Shield and Amperka technical support, this was the only solution. After our trials, a revised version of the library was released with support for the work on the iron serial, but ... it never earned either Due or Mega. What is difficult to explain at all - if the module works on SoftSerial, then what prevents it from working on the iron Serial?

The third "but" conceptual. That's not all. the main ambush lies in the principle of the library module. It works in a closed mode, that is, it blocks the operation of the controller while waiting for an SMS (and this is 99% of the time) and during all other operations of the module. What does it mean? This means that you cannot build anything except a test sketch on the standard library from the manufacturer. A strategic chicken coop does not shine for you, as thousands more cool applications like security systems, greenhouse management, GSM home control, etc., etc. do not shine.

More about SoftwareSerial


On the Arduino Due module does not work, so we will talk about the Arduino Mega. The piquancy of the situation lies in the fact that having Mega's three free hardware ports Serial1, Serial2 and Seria3, we are forced to connect and use the SoftwareSerial library. No imaginable and unthinkable ways, including rearranging the jumpers on the module and interrogating with Amperka's technical support addiction, failed to get GPRS Shield to work with hardware ports on the Arduino Mega.

Okay ... We use the SoftwareSerial, but even here an ambush awaits us. SoftwareSerial can work on many Arduino Mega pins, but for some reason it works with the module only on the 62-m and 63-m pins. Why only on them? This riddle is great there. We, apparently, will never know the answer to this question.

Flour choice


And so, as usual, we come to the understanding that the standard library is unsuitable for practical use. By blocking the controller for the time it sends it to wait for a response from the GPRS module, the library blocks all other processes. The controller simply drops out of reality 99% of the time, waiting for a request or an answer to arrive or the timeout will not end.

This puts an end to any application of GPRS Shield, except for the test: sent a request to turn on the outlet - it turned on, sent a request to turn off - it turned off. There is no question of any web server work, work with sensors, actuators control, etc. None of this will work.

The question came up with an edge - either we refuse the manufacturer’s library and write our GPRS Shield control code (easy to say), or we abandon the module itself and look for an alternative solution. But the module is already there, so it was decided to abandon the library and write its own control code for the module.

Conquest of Everest


I miss the whole process of creating code that replaces the standard library from Amperka, I will only note that it was very, very difficult and required considerable research, a sophisticated algorithm and considerable effort to test the resulting solution.

For you, everything will be simple - a little magic and the ready-made working code of the AMS module for GPRS Shield support is ready.

image

Full code of the AMS module, replacing the standard library
/* Modul GPRS part of Arduino Mega Server project */ #ifdef GPRS_FEATURE #include <SoftwareSerial.h> #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 


The module was developed and tested on AMS version 0.16 for Arduino Mega. Testing showed absolutely stable and reliable operation of GPRS Shield under the control of this module.

Connect to the AMS module support GPRS Shield


In order to connect the GPRS Shield module to the Arduino Mega Server, you need to perform a few simple steps. First, add the following lines to the AMS main file.

 #define GPRS_FEATURE 

and

 byte modulGprs = MODUL_NOT_COMPILLED; 

in the appropriate sections. There you also need to add test variables that in your real project will be responsible for the controlled parameters (temperature, contact status, etc.).

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

Explanation of supported features


The module contains a basic set of requests and commands, but you can replace these commands with any others and / or extend the basic set with commands and requests you need.

Requests


Requests are sent via SMS, the system sends replies (also via SMS) containing information about the requested parameter. Very simple: we send a request “temp1” from the phone, the system in response sends the current temperature value temp1=20.50 .

temp1 - temperature request
cont1 - request for contact status
leak1 - request for leakage sensor status
smoke1 - request for smoke sensor status
relay1 - request for relay status (key)
servo1 - request servo servo status
period - request for the period of automatic telemetry parcels
all - request of all system parameters

In response to the “all” request, the system sends a list of all parameters. In the case of sending an unregistered command, the system will send the answer “Error”. If the monitored parameters are outside the normal range, the system itself can send alarm messages to the operator’s smartphone.

In order to manage the system, only a registered operator could control the system from unauthorized access - it responds only to commands from a specific phone number (s).

Teams


Commands are sent via SMS, accepting a command, the system changes its state and sends answers with information about the new status of its actuators or settings.

relay1 = - relay control command (key)
servo1 = - servo drive control command
period = - command to change the telemetry auto-posting period

Example. Command relay1=1 , response relay1=ON . Command relay1=0 , response relay1=OFF .

Explanation of the code


Initialization of the GPRS module. The standard initialization of the AMS module, the launch of the SoftwareSerial, and some GSM-AT commands. The purpose of this block is to bring the GPRS module to life and return it from nirvana if it has gone to it for some reason beyond our control.

 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); } 

The main working function of the module. Every 20 seconds, the Arduino Mega Server checks for incoming SMS messages and, if any requests or commands arrive, executes them. As a result, the average delay in the execution of a command is 10 seconds (without taking into account the delay in transmission by the SMS service provider). This interval can be adjusted, 20 seconds are chosen as a compromise between the speed of response to SMS commands and the system load.

These 20 seconds AMS can do everything that it needs, and SMS that arrives during this period of time is not lost. When using the standard library, the system can do nothing except wait for the arrival of SMS, otherwise it just loses them (and the controller at this time also can not do anything).

Immediately there is a telemetry sending code with a specified interval of 1 hour, 6 hours, 12 hours or 24 hours. This interval can be changed by sending an appropriate command via 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; } } 

The interaction between the GPRS module and the controller is performed using special AT commands and the following functions form the GPRS commands to the module in codes that are understandable to it.

The function of sending SMS. In the parameters of the function, the destination phone number and the command itself or the request are transmitted in text form.

 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 reading function. Accepted values ​​are placed in the message, phone, and datetime variables, which contain, respectively, the incoming command, phone number, and the time of the parcel.

 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")); } } 

The function of forming a response to SMS requests and commands. This function is specific to each project and you can change it in your projects in accordance with the logic of your system.

 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")); } } 

Functions forming answers. These are the functions that form the answers that the system sends via SMS to the user's smartphone in response to a request or at its discretion, in accordance with the program.

 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() 

And the function of executing commands. These are functions that are performed by SMS commands and change the state of the system, i.e., change its settings or enable or disable actuators.

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

The remaining functions are purely technical, doing all the rough work of maintaining the interaction between the GPRS Shield and the microcontroller.

More about SoftwareSerial


All of the above is not enough for GPRS Shield to work in transparent mode and stop blocking other processes running on the microcontroller. It is necessary to modify the code of the SoftwareSerial library. The fact is that its standard buffer of 64 bytes is not enough to digest most AT GSM commands and maintain dynamic interaction between the module and the controller.

You need to increase this buffer to at least 128 bytes, and only after that the magic will work and GPRS Shield will begin to work normally in transparent mode. This is done in the file SoftwareSerial.h . Row

 #define _SS_MAX_RX_BUFF 64 // RX buffer size 

need to change to

 #define _SS_MAX_RX_BUFF 128 // RX buffer size 

Conclusion


You see the exchange of operator commands and system responses on the smartphone screen. And at the same time, the system performs dozens of processes in a pseudo-multitasking mode. And GPRS Shield began to occupy less than 1% of the processor time, instead of 99% as before, freeing up the rest of the time for the web server and other tasks for managing the chicken coop.

image

That's all, we integrated GPRS Shield into AMS , overcame all obstacles, forced it to work in transparent mode, without blocking other processes like a web server, wireless sensors and actuators, and this opens up attractive prospects for building a huge number of systems with GSM SMS control and control on the basis of AMS - greenhouses, security and heating systems, various options for a smart home, etc., etc., almost to infinity.

Source: https://habr.com/ru/post/401721/


All Articles