sim800a¶
Product page - Unknown Datasheet - Unknown Pinout - On board
Като цяло работата с GSM модул и ардуино е сравнително лесна, освен тогава когато не е...
На пазара има няколко разновидности модеми (TODO>списък), но по-интересен е броя на различните дизайни (PCB Designs) които се предлагат от различните производители (които също са безброи). Това което имам впредвид е, че два продукта базирани на един и същ модем (например SIM800), и именувани по сходен начин, са до-голяма степен различни в съшността си. (съответно и начина по който можем да ги използваме. )
Тези различия често са достатъчно осезаеми и напълно достатъчни, че да провалят проекта ни, в случай, че не знаем за тях и не ги предвидим в първоначалното планиране на цялостната работа.
Следващите редове се базират изцяло на наблюденията ми през последните три години на работа с различни "IoT" устройства (2018-2021).
Въпросните различния се дължат на това, че: първо - различните производители имплементират (задължителната) периферията на модема по (достатъчно) различен начин, че това да промени крайното(по-нататъшЧното!?!?!?) действеи и/или начин на работа на/със въпросния продукт (за нас-девелопърите), второ - често биват добавяни различни функционалности към крайния (за developer-а е междинен...) продукт, които не са предвидени по начало в основния такъв (в случея това е GSM модема). Най-вероятно си мислите "какво от това? нали това е смисъла на развоините платки, пък и преди да излязат на пазара минават регулационен процес от съответните органи"...; трето-прекалено често крайният продукт съдържа критични грешки в дизайна на схемата (напр. LED в esp8266 платки), съответно и в начина на работа след това и четвърто, но не на последно място - много често липсва каквато и да е документация на продукта (било то схема на I/O или описание на наличните функционалности за следващия разработчик)
Модемът с който ще работим е един от най-известните (най-често срещаните) - SIM800 и по-точно SIM800**A**, а модула (до колкото е възможно да бъде идентифициран) е по дизайн на UNV, конкретно този с който разполагаме е версия V3.9.3 от 26-08-2016г. (предполагам, че годината се отнася към версията, а не дата на производство)
значението на буквата в края (как се нарича?индекс може би?), и какви други възможности има, ще разгледаме по-късно.
Предстой да разгледаме основни
Тестване на модула¶
Нужни¶
- USB Type Mini B
- SIM Card with atleast GPRS data
- Few jumper wires
- Putty
- КОНВЕРТОР USB КЪМ UART
Свързване¶
След като модулът е свързан чрез конвертора към компютъра, сме готови да тестваме изправността и функционалността му. За целта отваряме Device Manager и проверяваме номера на COM порта. За връзка ще използваме програмата Putty
Натискаме Open. Трябва да изчакаме няколко секунди докато се осъществи връзка към модула. Разбираме, че връзката е осъществена когато въведем някакъв символ и той се визуализира в командния ред.
Основни АТ команди за тестване на изправността и функционалността.¶
В таблицата са събрани основните команди чрез които ще съберем основна информация за версията на фирмуера на устройството и други.
Команда | Използва се за |
---|---|
AT | handshake test |
AT+CSQ | Signal Quality |
AT+CCID | Read SIM Information to confirm its plugged in |
AT+CREG? | Check if SIM is registered in the network |
AT - Това е най-основната AT команда. Тя също така инициализира Auto-baudrate. Ако тя работи, трябва да видите ехото на AT символите и след това OK, което ви казва, че всичко е наред и тя ви разбира правилно! След това можете да изпратите някои команди за запитване към модула и да получите информация за него, като например:
AT+CSQ - Проверете "силата на сигнала" - първото # е силата на сигнала в dB, тя трябва да е по-висока от около 5. По-високата стойност е по-добра. Разбира се, това зависи от вашата антена и местоположение.
AT+CCID - получаване на номера на SIM картата - с тази функция се проверява дали SIM картата е поставена правилно и можете да проверите дали номерът е записан на картата.
AT+CREG? Проверява дали сте регистрирани в мрежата. Вторият # трябва да бъде 1 или 5. 1 означава, че сте регистрирани в домашната мрежа, а 5 - че сте регистрирани в роуминг мрежа. Другите две числа освен тези показват, че не сте регистрирани в никаква мрежа.
Интегриране на модула към IoT контролер¶
Тоест ще използваме GSM модула за да контролираме I/O на Arduino UNO например.
Нужни¶
- Всичко от предишната точка.
- Arduino UNO, може и клонинг. ( не забравяйте да дарите някой лев в този случай :) )
- Още няколко джъмпера.
- Нисковолтово реле.
1. Изпращане на комнди от Arduino¶
За начало ще изпълним тестовите АТ команди, които коментирахме по-горе, но ще ги изпратим от Ардуино към модула. Освен, че ще изпратим командите искаме и да прочетем отговора на модула, които да се изпише в серийния монитор на Arduino IDE.
Стъпка 1¶
Качваме следния код на Arduino UNO.
#include <SoftwareSerial.h>
//Create software serial object to communicate with SIM800A
SoftwareSerial mySerial(3, 2); //SIM800A Tx & Rx is connected to Arduino #3 & #2
void setup()
{
//Begin serial communication with Arduino and Arduino IDE (Serial Monitor)
Serial.begin(9600);
//Begin serial communication with Arduino and SIM800А
mySerial.begin(9600);
Serial.println("Initializing...");
delay(1000);
mySerial.println("AT"); //Once the handshake test is successful, it will back to OK
updateSerial();
mySerial.println("AT+CSQ"); //Signal quality test, value range is 0-31 , 31 is the best
updateSerial();
mySerial.println("AT+CCID"); //Read SIM information to confirm whether the SIM is plugged
updateSerial();
mySerial.println("AT+CREG?"); //Check whether it has registered in the network
updateSerial();
}
void loop()
{
updateSerial();
}
void updateSerial()
{
delay(500);
while (Serial.available())
{
mySerial.write(Serial.read());//Forward what Serial received to Software Serial Port
}
while (mySerial.available())
{
Serial.write(mySerial.read());//Forward what Software Serial received to Serial Port
}
}
Към момента можете да пуснете сериен монитор, но това което ще видите е просто ред който гласи Initializing . . .
и нищо повече. Трябва да свържем контролера към модула и релето.
Стъпка 2¶
Свързване на GSM Модула към Arduino UNO
Преди да започнете да свързвате каквото и да е, моля изключете всичко от захранването.
Пазете компонентите си! Въпреки, че цената им е сравнително ниска, няма нужда да разхищаваме ресурси.
Дали можем да захранваме модула от ардуиното е спорен въпрос, който се обсъжда из форумите в интернет от години и няма ясен отговор. Според документацията на компонентите - НЕ! Според практиката - да .. Това което ще направим ние е да свържем модула директно към контролера, при мен няма никакви проблеми ползвайки го по този начин, постъпете по ваше усмотрение. Другият вариянт (който е и по-добрия, в случай, че имате възможност - направете го) е да осигурим отделно захранване за GSM модула и контролера.
Следва ASCII диаграма на свързването между двата компонента.
Стъпка 3¶
Използвване на серийния монитор за наблюдение на работата
След като свържем GSM модула към Aduino UNO и захраним установката, наблюдаваме следния резултат в серийния монитор (това е успешен резултат):
Серийният монитор трябва да бъде настроен по следния начин: Both NL & CR, 9600 Baudrate
2.Контролиране на реле модул¶
Ще използваме Arduino UNO за да имплементираме код разбиращ няколко команди.
Свързане¶
Следва диаграма на свързките между релето и контролера.
Основен код¶
Следващият код ще изпозлваме като основа за следващите примери, в който ще добавим приемане на команда през серийния порт, т.н. ...
int RelayPin = 7;
void setup() {
// Initialize serial connection at 9600 baudrate
Serial.begin(9600);
// Confirm that serial comm. is established
Serial.println("Serial communication established.");
// Set RelayPin as an output pin
Serial.println("Setting RelayPin as output");
pinMode(RelayPin, OUTPUT);
}
void loop() {
// Turn on the relay...
Serial.println("Turning the relay on (digitalWrite)");
digitalWrite(RelayPin, LOW);delay(400);
// Reading pin state with digitalRead and returning the current state.
Serial.println("current state");delay(100);
Serial.println(digitalRead(RelayPin));delay(2500);
// Turn off the relay...
Serial.println("Turning the relay off (digitalWrite)");
digitalWrite(RelayPin, HIGH);delay(400);
// Reading pin state with digitalRead and returning the current state.
Serial.println("current state");delay(100);
Serial.println(digitalRead(RelayPin));delay(2500);
}
Резултат¶
Резултата наблюдаван в серийния монитор:
Реални снимки на установката:
Кратко видео по време на операция:
2.1.Имплементиране на командите¶
Ще използваме серийния монитор за да контролираме реле модула по команда
Ще използваме вече познатия начин за получаване на команди (със знак за старт и край на съобщението - <>)
Командите които ще имплементираме са две: за включване на релето и за изключване, съответно relay_on
и relay_off
. Освен тва, ще направим така, че платката да изписва състоянието на пина след като командата бъде изпълнена. Всичко което не е разпонато от командната логика ще попада в else блока на кода и ще връща NOT IMPLEMENTED
.
Серийният монитор трябва да бъде настроен на край на реда - Newline
Код за този пример:
int RelayPin = 7;
// size of recv buffer
const byte numChars = 32;
// array for storing parsing chars
char receivedChars[numChars];
// temporary array for use when parsing
char tempChars[numChars];
// variables to hold the parsed data
char messageFromPC[numChars] = {0};
int integerFromPC = 0;
float floatFromPC = 0.0;
/*
* variable which hold the state if we are currently
* receiving or not, i.e. if we already received already
*/
boolean newData = false;
void setup() {
// Initialize serial connection at 9600 baudrate
Serial.begin(9600);
delay(100);
// Confirm that serial comm. is established
Serial.println("Serial communication established.");
// Set RelayPin as an output pin
Serial.println("Setting RelayPin as output");
pinMode(RelayPin, OUTPUT);
}
void loop() {
/* v1 Code
// Turn on the relay...
Serial.println("Turning the relay on (digitalWrite)");
digitalWrite(RelayPin, LOW); delay(400);
// Reading pin state with digitalRead and returning the current state.
Serial.println("current state"); delay(100);
Serial.println(digitalRead(RelayPin)); delay(2500);
// Turn off the relay...
Serial.println("Turning the relay off (digitalWrite)");
digitalWrite(RelayPin, HIGH); delay(400);
// Reading pin state with digitalRead and returning the current state.
Serial.println("current state"); delay(100);
Serial.println(digitalRead(RelayPin)); delay(2500);
*/
// this is the function which receive the data from serial monitor
recvWithStartEndMarkers();
if (newData == true) { // if this is true it means we are currently receiving data
strcpy(tempChars, receivedChars);
// this temporary copy is necessary to protect the original data
// because strtok() used in parseData() replaces the commas with \0
// this function parse the received data and save in to appropriate var
parseData();
// this function show the saved data from the variables, later i
// will be used for sending it to mqtt or any different post process
showParsedData();
newData = false; // after we have finished with any processing of the
// data we set the variable to false
}
}
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false) {
rc = Serial.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
void parseData() { // split the data into its parts
char * strtokIndx; // this is used by strtok() as an index
strtokIndx = strtok(tempChars, ","); // get the first part - the string
strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
integerFromPC = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ",");
floatFromPC = atof(strtokIndx); // convert this part to a float
}
// Here is where we are going to implement our command set
// for controlling whatever we want or have to
void showParsedData() {
// check the first part if it this string
if (String(messageFromPC).equals("relay_on")) { // Convert to String so we can compare
// here we have to implement all further logic for controlling the desired appliance
// i.e. what should the board do when we send this ^ command
Serial.print("Command received ");
Serial.println(messageFromPC);
delay(10);
digitalWrite(RelayPin, LOW);
delay(10);
Serial.println(digitalRead(RelayPin));
}
else if (String(messageFromPC).equals("relay_off")) { // Convert to String so we can compare
// here we have to implement all further logic for controlling the desired appliance
// i.e. what should the board do when we send this ^ command
Serial.print("Command received ");
Serial.println(messageFromPC);
delay(10);
digitalWrite(RelayPin, HIGH);
delay(10);
Serial.println(digitalRead(RelayPin));
}
else {
Serial.println("\n\nNOT IMPLEMENTED\n\n");
return;
}
}
//// this function can be used as starter point when u implement ur own logic
//void showParsedData() {
// Serial.print("Message ");
// Serial.println(messageFromPC);
// Serial.print("Integer ");
// Serial.println(integerFromPC);
// Serial.print("Float ");
// Serial.println(floatFromPC);
//}
2.2.Получаване на новопристигнал СМС и прочитане на съдържанието му¶
Следващият код изпълнява тестовите АТ команди за да провери функцилността и готовността за работа на установката. След това поставя модула в текстов режим и прочита СМС когато той е получен.
Код¶
TODO
Към момента (14;45, 23-12-2021) програмата не работи по очаквания начин. Проблемът е, че не чете пълното съдържание на получения СМС!
Резултат¶
Наблюдение на операцията на системата през серийния монитор:
2.3.Контролиране на реле модул чрез SMS¶
На теория ще обединим всички примери в една програма.
Програма за Arduino Uno която чете SMS веднага щом той е получен. Обработва информацията, запазва САМО съдържанието на съобщението в променлива от тип String и позволява изпълянване на логика при наличие на ключова дума в това съдържание.
Код¶
// The library which makes the software serial connection on pins 2,3
#include <SoftwareSerial.h>
//Create software serial object to communicate with SIM800A
SoftwareSerial mySerial(3, 2); //SIM800A Tx & Rx is connected to Arduino #3 & #2
const int RelayPin = 7;
void setup() {
// Set relay as OUTPUT
pinMode(RelayPin, OUTPUT);
// By default the relay is off
digitalWrite(RelayPin, LOW);
// put your setup code here, to run once:
// Begin serial communication with Arduino and Arduino IDE (Serial Monitor)
Serial.begin(9600);
//Begin serial communication with Arduino and SIM800А
mySerial.begin(9600);
Serial.println("Initializing...");
delay(1000);
mySerial.println("AT"); //Once the handshake test is successful, it will back to OK
updateSerial();
mySerial.println("AT+CSQ"); //Signal quality test, value range is 0-31 , 31 is the best
updateSerial();
mySerial.println("AT+CCID"); //Read SIM information to confirm whether the SIM is plugged
updateSerial();
mySerial.println("AT+CREG?"); //Check whether it has registered in the network
updateSerial();
mySerial.println("AT+CMGF=1"); // Set the modem in TEXT MODE
updateSerial();
// To read newly arrived SMS :
mySerial.println("AT+CNMI=2,2,0,0,0\r");
updateSerial();
delay(1000);
}
// put your main code here, to run repeatedly:
void loop() {
updateSerial();
String theMessage = read_sms();
Serial.println(theMessage);
if (theMessage.indexOf("relay_off") >= 0)
{
Serial.println("THE MSG CONTAINS THE KEYWORD");
digitalWrite(RelayPin, LOW);
Serial.println("LOW,OFF");
// Serial.print("State after action");
// Serial.println(digitalRead(RelayPin));
}
else if (theMessage.indexOf("relay_on") >= 0)
{
Serial.println("THE MSG CONTAINS THE KEYWORD");
digitalWrite(RelayPin, HIGH);
Serial.println("HIGH,ON");
// Serial.print("State after action");
// Serial.println(digitalRead(RelayPin));
}
}
String read_sms()
{
int x = 0; // sms character counter
String sms = ""; //initialize
unsigned long int prv_millis = millis(); //wait initialization
while (1)
{
if (mySerial.available() > 0)
// if(Serial.available() >0)
{
char incoming_sms = mySerial.read();
//char incoming_sms=Serial.read();
sms += incoming_sms; //add up all the characters as string
prv_millis = millis();
x++; // increment sms character counter
}
unsigned long int new_millis = millis();
if ((x > 0) && ((new_millis - prv_millis) > 200)) //if a single letter has been received and no character been
//received since last 200 msec, break
break;
}
//Serial.print("received sms =");
//Serial.println(sms);
int m = 1 + sms.lastIndexOf('"');
// find location of last ' " ' of sms format
// [ +CMGL: 1,"REC UNREAD","+XXXXXXXXXXXX",,"01/01/21,09:55:16+08"
// Test message 1 ]
int n = sms.length(); // find the length of sms
// Serial.print("actual sms =");
//Serial.println(sms.substring(m,n)); // sms text lies between the two
return (sms.substring(m, n)); //return the sms
}
void updateSerial() {
delay(500);
while (Serial.available())
{
//Forward what Serial received to Software Serial Port
mySerial.write(Serial.read());
}
while (mySerial.available())
{
//Forward what Software Serial received to Serial Port
Serial.write(mySerial.read());
}
}
Стъпка 3 Трябва да бъде отделна точка¶
Добавяме Реле модула към установката.
Диаграма на свързването