Arduino Uno und Siemens TC35: SMS empfangen und Schalter steuern

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

  1#include <SoftwareSerial.h>
  2#define rxPin 2
  3#define txPin 3
  4#define statusPin 13
  5#define igtPin 8
  6
  7String accessPin("1234");
  8String cmdOff("OFF");
  9String cmdOn("ON");
 10String cmdStatus("STATUS");
 11
 12SoftwareSerial gsmSerial(rxPin, txPin);
 13
 14String smsNumber;
 15String smsText;
 16String cmdMessage;
 17String cmdResponse;
 18
 19int nMessages;
 20char c;
 21int state;
 22int switchStatus;
 23
 24// -------------------------------------------------------------
 25void setup()
 26{
 27  Serial.begin(9600);
 28  while(!Serial) {};
 29  gsmSerial.begin(9600);
 30
 31  pinMode(statusPin, OUTPUT);
 32  digitalWrite(13, LOW);
 33  switchStatus = false;
 34  
 35  // TC35 einschalten, igtPin 100ms auf GND ziehen
 36  pinMode(igtPin, INPUT);
 37  digitalWrite(igtPin, LOW);
 38  pinMode(igtPin, OUTPUT);
 39  delay(100);
 40  pinMode(igtPin, INPUT); 
 41  delay(5000);
 42
 43  // Format Text fuer SMS einstellen
 44  SendCmd("AT+CMGF=1");
 45
 46  CountMessages();
 47  DeleteAllMessages();
 48
 49  Serial.println("Ready");
 50}
 51
 52// -------------------------------------------------------------
 53void loop()
 54{
 55  if (nMessages > 0)
 56  {
 57    ReadSms(nMessages);
 58    Serial.println("Incoming: [" + smsNumber + "] [" + smsText + "]");
 59    DeleteMessage(nMessages);
 60    nMessages--;
 61    if (smsText.length() >= accessPin.length() && smsText.startsWith(accessPin))
 62    {
 63      smsText.remove(0, accessPin.length());
 64      if (smsText == cmdOn)
 65      {
 66        digitalWrite(13, HIGH);
 67        switchStatus = true;
 68      }
 69      else if (smsText == cmdOff)
 70      {
 71        digitalWrite(13, LOW);
 72        switchStatus = false;
 73      }
 74      else if (smsText == cmdStatus)
 75      {
 76        smsText = "Switch is ";
 77        if (switchStatus == true)
 78        {
 79          smsText += cmdOn;
 80        }
 81        else
 82        {
 83          smsText += cmdOff;
 84        }
 85        SendMessage();
 86      }
 87      else
 88      {
 89        smsText = "Available commands: " + cmdOn + ", " + cmdOff + " und " + cmdStatus;
 90        SendMessage();
 91      }
 92    }
 93  }
 94  else
 95  {
 96    delay(5000);
 97  }
 98  CountMessages();
 99}
100
101// -------------------------------------------------------------
102void SendCmd(String cmd)
103{
104  gsmSerial.println(cmd);
105  delay(1000);
106  while(gsmSerial.available() > 0)
107  {
108    c = gsmSerial.read();
109  }
110  delay(100);
111}
112
113// -------------------------------------------------------------
114void CountMessages()
115{
116  nMessages = 0;
117  cmdMessage = "AT+CPMS=\"SM\"";
118  gsmSerial.println(cmdMessage);
119  delay(1000);
120
121  cmdResponse = "";
122  state = 0;
123
124  String num = "";
125  while(gsmSerial.available() > 0)
126  {
127    c = gsmSerial.read();
128    if (c != 13 && c != 10)
129    {
130      cmdResponse += c;
131    }
132    
133    if (c == ':' && state == 0)
134    {
135      // Leerzeichen vor der ersten Zahl
136      c = gsmSerial.read();
137      state = 1;
138    }
139    if (c != ' ' && state == 1)
140    {
141      // Anfang der ersten Zahl
142      state = 2;
143    }
144    if (c == ',' && state == 2)
145    {
146      // Ende der ersten Zahl
147      state = 3;
148    }
149    if (state == 2)
150    {
151      num += c;
152    }
153  }
154  nMessages = num.toInt();
155}
156
157// -------------------------------------------------------------
158void DeleteAllMessages()
159{
160  while(nMessages > 0)
161  {
162    DeleteMessage(nMessages);
163    nMessages--;
164  }
165}
166
167// -------------------------------------------------------------
168void DeleteMessage(int smsIndex)
169{
170  cmdMessage = "";
171  cmdMessage = smsIndex + cmdMessage;
172  cmdMessage = "AT+CMGD=" + cmdMessage;
173  SendCmd(cmdMessage);
174}
175
176// -------------------------------------------------------------
177void ReadSms(int smsIndex)
178{
179  cmdMessage = "";
180  cmdMessage = smsIndex + cmdMessage;
181  cmdMessage = "AT+CMGR=" + cmdMessage;
182  gsmSerial.println(cmdMessage);
183  delay(1000);
184  
185  smsNumber = "";
186  smsText = "";
187  cmdResponse = "";
188  
189  state = 0;
190  // Format: +CMGR: "REC READ","+336xxxxxxxx",,"13/06/10,22:18:35+08"<CR><LF>Message<CR>
191  while(gsmSerial.available() > 0)
192  {
193    c = char(gsmSerial.read());
194    if (c != 13 && c != 10)
195    {
196      cmdResponse += c;
197    }
198    
199    if (c == ',' && state == 0)
200    {
201      // Anfang der Telefonnummer
202      c = char(gsmSerial.read());  // " ueberlesen
203      c = char(gsmSerial.read());
204      state = 1;
205    }
206    if (c == '"' && state == 1)
207    {
208      // Ende der Telefonnummer
209      state = 2;
210    }
211    if (c == 13 && state == 2)
212    {
213      // Anfang Text der SMS
214      c = char(gsmSerial.read());  // <LF> ueberlesen
215      state = 3;
216    }
217    if (c == 13 && state == 3)
218    {
219      // Ende Text der SMS
220      state = 4;
221    }
222    
223    switch(state)
224    {
225      case 1:
226        smsNumber += c;
227        break;
228      case 3:
229        smsText += c;
230        break;
231    }
232  }
233  
234  smsText.toUpperCase();
235  smsText.trim();
236}
237
238// -------------------------------------------------------------
239void SendMessage()
240{
241  if (smsText.length() > 0)
242  {
243    Serial.println("Outgoing: [" + smsText + "] an " + smsNumber);
244
245    cmdMessage = "AT+CMGS=\"" + smsNumber + "\"\r";
246    gsmSerial.print(cmdMessage);
247    delay(1000);
248    gsmSerial.print(smsText);
249    delay(1000);
250    gsmSerial.println((char)26);
251    delay(2000);
252    while(gsmSerial.available() > 0)
253    {
254      c = gsmSerial.read();
255    }
256  }
257}

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.