Im vorhergehenden Teil dieser Reihe wurde erklärt, wie man mit einem Arduino Uno und einem GSM-Board (in diesem Fall mit dem Siemens TC35) SMS versenden kann. Heute nun soll das Programm dahingehend erweitert werden, dass SMS empfangen und ausgewertet werden können und anhand des SMS-Inhalts ein Schalter bzw. ein Pin des Arduino geschaltet werden kann.
Der Schaltungsaufbau ist der gleich, wie er im letzten Versuch verwendet wurde. Hinzugekommen ist ein größeres Stück Software für den Arduino, welches nun die erforderlichen Befehle zum Empfang von SMS an das GSM-Modem sendet.
Die zur Kommunikation verwendete Lib SoftwareSerial definiert nur einen 64 Byte großen Empfangspuffer. Für den Empfang von SMS ist das viel zu wenig. In der Datei hardware/arduino/avr/libraries/SoftwareSerial/SoftwareSerial.h wird die Puffergröße durch _SS_MAX_RX_BUFF festgelegt. Diese Puffergröße sollte dort auf mindestens 1024 festgelegt werden.
#define _SS_MAX_RX_BUFF 1024
Folgende Vorgehensweise im Arduino-Programm für die Initialisierung wurde gewählt:
- Aktivieren der seriellen Schnittstellen zum GSM-Modem wie auch zum Computer (für die Protokollierung und zu Testzwecken)
- Einschalten des GSM-Teils des TC35 über den IGT-Pin
- Löschen aller alten bereits auf der SIM-Karte gespeicherten SMS-Nachrichten
Im anschließenden loop des Arduino wird ermittelt, ob neue SMS-Nachrichten vorliegen. Diese sollen dem Schema “PIN{ ON | OFF | STATUS }” ensprechen. Bei korrekt übermittelter PIN wird bei falschem Kommando eine SMS mit entsprechendem Hilfetext zurückgesendet. ON bewirkt das Einschalten des Schalter, OFF das Ausschalten und STATUS sendet eine SMS mit dem aktuellen Zustand des Schalters an den Sender der SMS zurück.
Arduino-Programm
#include <SoftwareSerial.h>
#define rxPin 2
#define txPin 3
#define statusPin 13
#define igtPin 8
String accessPin("1234");
String cmdOff("OFF");
String cmdOn("ON");
String cmdStatus("STATUS");
SoftwareSerial gsmSerial(rxPin, txPin);
String smsNumber;
String smsText;
String cmdMessage;
String cmdResponse;
int nMessages;
char c;
int state;
int switchStatus;
// -------------------------------------------------------------
void setup()
{
Serial.begin(9600);
while(!Serial) {};
gsmSerial.begin(9600);
pinMode(statusPin, OUTPUT);
digitalWrite(13, LOW);
switchStatus = false;
// TC35 einschalten, igtPin 100ms auf GND ziehen
pinMode(igtPin, INPUT);
digitalWrite(igtPin, LOW);
pinMode(igtPin, OUTPUT);
delay(100);
pinMode(igtPin, INPUT);
delay(5000);
// Format Text fuer SMS einstellen
SendCmd("AT+CMGF=1");
CountMessages();
DeleteAllMessages();
Serial.println("Ready");
}
// -------------------------------------------------------------
void loop()
{
if (nMessages > 0)
{
ReadSms(nMessages);
Serial.println("Incoming: [" + smsNumber + "] [" + smsText + "]");
DeleteMessage(nMessages);
nMessages--;
if (smsText.length() >= accessPin.length() && smsText.startsWith(accessPin))
{
smsText.remove(0, accessPin.length());
if (smsText == cmdOn)
{
digitalWrite(13, HIGH);
switchStatus = true;
}
else if (smsText == cmdOff)
{
digitalWrite(13, LOW);
switchStatus = false;
}
else if (smsText == cmdStatus)
{
smsText = "Switch is ";
if (switchStatus == true)
{
smsText += cmdOn;
}
else
{
smsText += cmdOff;
}
SendMessage();
}
else
{
smsText = "Available commands: " + cmdOn + ", " + cmdOff + " und " + cmdStatus;
SendMessage();
}
}
}
else
{
delay(5000);
}
CountMessages();
}
// -------------------------------------------------------------
void SendCmd(String cmd)
{
gsmSerial.println(cmd);
delay(1000);
while(gsmSerial.available() > 0)
{
c = gsmSerial.read();
}
delay(100);
}
// -------------------------------------------------------------
void CountMessages()
{
nMessages = 0;
cmdMessage = "AT+CPMS=\"SM\"";
gsmSerial.println(cmdMessage);
delay(1000);
cmdResponse = "";
state = 0;
String num = "";
while(gsmSerial.available() > 0)
{
c = gsmSerial.read();
if (c != 13 && c != 10)
{
cmdResponse += c;
}
if (c == ':' && state == 0)
{
// Leerzeichen vor der ersten Zahl
c = gsmSerial.read();
state = 1;
}
if (c != ' ' && state == 1)
{
// Anfang der ersten Zahl
state = 2;
}
if (c == ',' && state == 2)
{
// Ende der ersten Zahl
state = 3;
}
if (state == 2)
{
num += c;
}
}
nMessages = num.toInt();
}
// -------------------------------------------------------------
void DeleteAllMessages()
{
while(nMessages > 0)
{
DeleteMessage(nMessages);
nMessages--;
}
}
// -------------------------------------------------------------
void DeleteMessage(int smsIndex)
{
cmdMessage = "";
cmdMessage = smsIndex + cmdMessage;
cmdMessage = "AT+CMGD=" + cmdMessage;
SendCmd(cmdMessage);
}
// -------------------------------------------------------------
void ReadSms(int smsIndex)
{
cmdMessage = "";
cmdMessage = smsIndex + cmdMessage;
cmdMessage = "AT+CMGR=" + cmdMessage;
gsmSerial.println(cmdMessage);
delay(1000);
smsNumber = "";
smsText = "";
cmdResponse = "";
state = 0;
// Format: +CMGR: "REC READ","+336xxxxxxxx",,"13/06/10,22:18:35+08"<CR><LF>Message<CR>
while(gsmSerial.available() > 0)
{
c = char(gsmSerial.read());
if (c != 13 && c != 10)
{
cmdResponse += c;
}
if (c == ',' && state == 0)
{
// Anfang der Telefonnummer
c = char(gsmSerial.read()); // " ueberlesen
c = char(gsmSerial.read());
state = 1;
}
if (c == '"' && state == 1)
{
// Ende der Telefonnummer
state = 2;
}
if (c == 13 && state == 2)
{
// Anfang Text der SMS
c = char(gsmSerial.read()); // <LF> ueberlesen
state = 3;
}
if (c == 13 && state == 3)
{
// Ende Text der SMS
state = 4;
}
switch(state)
{
case 1:
smsNumber += c;
break;
case 3:
smsText += c;
break;
}
}
smsText.toUpperCase();
smsText.trim();
}
// -------------------------------------------------------------
void SendMessage()
{
if (smsText.length() > 0)
{
Serial.println("Outgoing: [" + smsText + "] an " + smsNumber);
cmdMessage = "AT+CMGS=\"" + smsNumber + "\"\r";
gsmSerial.print(cmdMessage);
delay(1000);
gsmSerial.print(smsText);
delay(1000);
gsmSerial.println((char)26);
delay(2000);
while(gsmSerial.available() > 0)
{
c = gsmSerial.read();
}
}
}
Implementierte Subrouinen:
- SendCmd sendet eine Zeichenkette (einen Befehl) an das GSM-Modem und “verschluckt” die Antwort des Modems
- CountMessages ermittelt mit Hilfe des Befehls AT+CPMS=“SM” die aktuelle Anzahl der auf der SIM-Karte gespeicherten SMS (genauer: im Speicherbereich SM)
- DeleteAllMessages löscht, wie der Name schon sagt, alle gespeicherten SMS
- DeleteMessage löscht eine einzelne SMS
- ReadSms ließt eine einzelne SMS aus dem SIM-Speicher aus und speichert die Absender-Telefonnummer in der Variablen smsNumber und den Text in smsText
- SendMessage verschickt eine Nachricht (smsText) an die in smsNumber eingetragene Telefonnummer
Im Kopfteil des Programms kann die PIN festgelegt werden, die zum Übermitteln von Kommandos in der SMS-Nachricht angegeben werden muss.
Im ersten Teil der Reihe wurde beschrieben, wie die Schaltung aufgebaut ist und wie mit dem TC35 SMS versendet werden können. Im dritten Teil der Reihe soll die vorhandene Schaltung um eine Relaiskarte erweitert werden, mit der auch größere Lasten geschaltet werden können.