本系列文章由ex_net(张建波)编写,转载请注明出处。
http://blog.csdn.net/ex_net/article/details/8586348
市面上的短信猫,大多是TC35i、Tc35的模块。下面的程序主要是针对西门子的TC35系列的GSM模块。GSM模块的通讯采用AT指令。上位机(PC、单片机、ARM等)通过串口与GSM模块连接。
上图是TC35i模块,淘宝上大多短信猫的里面都是这个模块+232电平转换电路+SIM卡座+天线部分等。
基于GSM短信的应用很多,例如远程控制、数据采集等等。GSM短信猫编程的难点在与“程序与GSM”通讯是异步的。
例如:
AT+CSQ 查询信号指令,PC通过RS232串口发送后,GSM短信猫要等一小会才回答,而且还可能不回答。
这样一来就导致编程复杂了。
下面的程序采用了串口中断接收、定时器、线程的方式配合。实现了接收短信,并把接收到的短信存在数据库中。然后定期发送短信。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.Windows.Forms;
- namespace SMS
- {
- public class GSMModem
- {
- private string protName;
- private int BaudRate;
- private System.Collections.ArrayList alRecevingSM;// 短信接收 缓冲区
- private System.Collections.ArrayList alSendSM; // 短信【待】发送 缓冲区
- private System.Collections.ArrayList alAuthorizationNumber; //授权号码,系统只会回复授权号码
- int WORK_STATE;//短信猫工作状态
- //错误的短信数,主要是未经授权的号码发来的短信
- private int ErrorSMCount = 0;
- public int _ErrorSMCount
- {
- get
- {
- return ErrorSMCount;
- }
- }
- /// <summary>
- /// 0=自检
- /// 1=接收
- /// 2=发送
- /// </summary>
- public int _WORK_STATE
- {
- get
- {
- return WORK_STATE;
- }
- }
- //接收和发送定时器不能同时工作,切记
- //短信接收 轮询 定时器,定时发送 AT+CGMR={0}
- System.Windows.Forms.Timer revTimer = new System.Windows.Forms.Timer();
- //短信发送 队列 定时器,定时发送 AT+CMGS={0}
- System.Windows.Forms.Timer sendTimer = new System.Windows.Forms.Timer();
- //短信猫自检
- System.Windows.Forms.Timer selfCheckingTimer = new System.Windows.Forms.Timer();
- //短信猫连接设备端口
- System.IO.Ports.SerialPort Port = new System.IO.Ports.SerialPort();
- //短信猫串口接收缓冲
- /*备注:该缓冲变量由串口DataReceived事件负责接收,具体的处理过程由
- * Timer 定时器内的代码具体执行
- */
- string temp_gsm_str_buf = "";
- //
- int CMGD_MAX_ID = 0;//短信卡最大SM容量,通常情况下设置为25
- int CMGD_ID = 1; //当前串口发送读取的短信ID号
- int GSM_RST_ACT = 0;
- int GSM_MAX_RST_ACT = 8;
- int CGMS_Count = 0; //短信发送数量
- public int _CGMS_Count
- {
- get
- {
- return CGMS_Count;
- }
- }
- public int _CMGD_MAX_ID
- {
- get
- {
- return CMGD_MAX_ID;
- }
- }
- public int _CMGD_ID
- {
- get
- {
- return CMGD_ID;
- }
- }
- public int _GSM_RST_ACT
- {
- get
- {
- return GSM_RST_ACT;
- }
- }
- public int _GSM_MAX_RST_ACT
- {
- get
- {
- return GSM_MAX_RST_ACT;
- }
- }
- #region 短信猫工作状态
- string INF_GSM_CSQ;
- string INF_GSM_CSCS;
- string INF_GSM_CSCA;
- string INF_GSM_CNMI;
- bool INF_GSM_State;
- public string _INF_GSM_CSQ
- {
- get
- {
- return INF_GSM_CSQ;
- }
- }
- public string _INF_GSM_CSCS
- {
- get
- {
- return INF_GSM_CSCS;
- }
- }
- public string _INF_GSM_CSCA
- {
- get
- {
- return INF_GSM_CSCA;
- }
- }
- public string _INF_GSM_CNMI
- {
- get
- {
- return INF_GSM_CNMI;
- }
- }
- public bool _INF_GSM_State
- {
- get
- {
- return INF_GSM_State;
- }
- }
- #endregion
- private int _CmgsSleepTime;
- public GSMModem(string protName,
- string BaudRate,
- int CMGD_MAX_ID,
- System.Collections.ArrayList RecevingSM,
- System.Collections.ArrayList SendSM,
- int revTimerInterval,
- int sendTimerInterval,
- int selfCheckingTimerInterval,
- int CmgsSleepTime,
- System.Collections.ArrayList alAuthorizationNumber
- )
- {
- _CmgsSleepTime = CmgsSleepTime;//AT+CMGS 指令发送后,稍微延迟一定时间,让短信猫做出反应
- this.alAuthorizationNumber = alAuthorizationNumber;//保存授权号码
- this.CMGD_MAX_ID = CMGD_MAX_ID;
- this.protName = protName;
- this.BaudRate = int.Parse(BaudRate);
- this.alRecevingSM = RecevingSM;
- this.alSendSM = SendSM;
- selfCheckingTimer.Enabled = false;
- revTimer.Interval = revTimerInterval;
- revTimer.Enabled = false;
- sendTimer.Interval = sendTimerInterval;
- sendTimer.Enabled = false;
- revTimer.Tick += revTimer_Tick; //挂接短信轮询 定时器
- sendTimer.Tick += sendTimer_Tick;
- selfCheckingTimer.Interval = selfCheckingTimerInterval;
- selfCheckingTimer.Tick += selfCheckingTimer_Tick;
- Port.DataReceived += Port_DataReceived;
- }
- public bool Start()
- {
- try
- {
- Port.PortName = protName;
- Port.BaudRate = BaudRate;
- Port.Open();//打开串口
- //短信猫 先自检,自检成功了,才能进行 接收 或 发送
- selfCheckingTimer.Enabled = true;
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.Message);
- return false;
- }
- return false;
- }
- public bool Stop()
- {
- sendTimer.Enabled = false;
- revTimer.Enabled = false;
- selfCheckingTimer.Enabled = false;
- Port.Close();
- CMGD_ID = 1;
- alRecevingSM.Clear();
- alSendSM.Clear();
- ErrorSMCount = 0;
- return true;
- }
- void Port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
- {
- System.IO.Ports.SerialPort serialPort = Port;
- int len = serialPort.BytesToRead;
- byte[] bs = new byte[len];
- serialPort.Read(bs, 0, len);
- if (len == 0)
- {
- return;
- }
- else
- {
- //接收处理
- string r = Encoding.ASCII.GetString(bs, 0, len);
- temp_gsm_str_buf += r;
- //接收数据处理
- //txtGsmRevLog.Invoke(new EventHandler(DoUpdate), r); //线程处理
- Console.Write(r);
- }
- }
- private bool AuthorizationPhoneNumber(string phoneID)
- {
- for (int i = 0; i < alAuthorizationNumber.Count; i++)
- {
- string s = alAuthorizationNumber[i].ToString();
- if (phoneID.Equals(s))
- return true;
- }
- return false;
- }
- bool DoGsmDataReceived()
- {
- //短信猫发回数据处理
- string str = temp_gsm_str_buf;
- if (str.Trim().StartsWith("+CMGR"))
- {
- int P1 = str.IndexOf("0891");
- if (P1 != -1)
- {
- str = str.Substring(P1);
- int P2 = str.IndexOf("\r\n");
- if (P2 != -1)
- {
- string uPDU = str.Substring(0, P2);
- string decMsg = PDUDecoding.DecodingMsg(uPDU);
- string[] content = decMsg.Split(',');
- string stime = content[1].ToString();
- string sphoneId = content[0].ToString().Replace("+86", "");
- string sgsmtxt = content[2].ToString();
- //object[] row = { stime, sphoneId, sgsmtxt };
- if (AuthorizationPhoneNumber(sphoneId))
- {
- SMDATA sm = new SMDATA();
- sm.sTime = stime;
- sm.sPoneID = sphoneId;
- sm.sContent = sgsmtxt;
- sm.pdu = uPDU;
- sm.sType = "Admin";
- alRecevingSM.Add(sm);
- }
- else
- {
- //未经授权的用户,10086短信,广告信息,垃圾短信,用户手机号码
- string[] gsmCmd = sgsmtxt.Trim().Split('+');
- string cmd = gsmCmd[0].ToString();
- if (cmd == "CX")
- {
- if (gsmCmd.Length == 1)
- {
- //仅限 用户手机号码
- SMDATA sm = new SMDATA();
- sm.sTime = stime;
- sm.sPoneID = sphoneId;
- sm.sContent = "CX"; //让BI查询车辆信息
- sm.pdu = uPDU;
- sm.sType = "User";
- alRecevingSM.Add(sm);
- }
- }
- else
- {
- Console.WriteLine("Error:" + stime + " " + sphoneId + " " + sgsmtxt);
- ErrorSMCount++;
- }
- }
- return true;
- }
- }
- }
- else
- {
- }
- return false;
- }
- bool DoGsmDataSend()
- {
- //短信猫发回数据处理
- string str = temp_gsm_str_buf;
- if (str.Trim().IndexOf("+CMGS") > -1)
- {
- temp_gsm_str_buf = "";
- CGMS_Count++;
- return true;
- }
- else
- {
- //接收到错误信息,可能是短信发送失败
- }
- temp_gsm_str_buf = "";
- return false;
- }
- //短信发送 指令发送定时器
- int SendID;
- int AT_CMGS_LEN;
- void sendTimer_Tick(object sender, EventArgs e)
- {
- WORK_STATE = 2;//设置短信猫工作模式为 发送
- if (DoGsmDataSend())
- {
- alSendSM.RemoveAt(SendID);
- }
- //没有发送的短信了
- if (alSendSM.Count == 0)
- {
- CMGD_ID = 1;
- sendTimer.Enabled = false; //关闭发送
- revTimer.Enabled = true; //打开接收
- }
- else
- {
- SendID = 0;
- SMDATA sendSM = (SMDATA)alSendSM[SendID];
- string sPoneID = sendSM.sPoneID;
- string sContent = sendSM.sContent;
- string msg = sContent;
- string pdu = PDUDecoding.GetPDUMsg(sPoneID, msg);
- AT_CMGS_LEN = PDUDecoding.getLenght(msg);
- GsmWrite(string.Format("AT+CMGS={0}\r", AT_CMGS_LEN.ToString()));
- Thread.Sleep(_CmgsSleepTime); //500延时,如果短信猫反应慢,设置为1000
- GsmWrite(pdu + "\r");
- }
- }
- //短信轮询 指令发送定时器
- void revTimer_Tick(object sender, EventArgs e)
- {
- //定时器每中断1次,接收1条指令
- WORK_STATE = 1;//设置短信猫工作模式为 接收
- if (DoGsmDataReceived())
- {
- int old_CMGD_ID = CMGD_ID - 1;
- GsmWrite("AT+CMGD=" + old_CMGD_ID.ToString() + "\r");
- Thread.Sleep(1000);
- }
- if (CMGD_ID > CMGD_MAX_ID)
- {
- revTimer.Enabled = false;//停止接收
- sendTimer.Enabled = true;//启动短信发送
- }
- else
- {
- //再读取下一条
- temp_gsm_str_buf = "";
- GsmWrite("AT+CMGR=" + CMGD_ID.ToString() + "\r");
- CMGD_ID++;
- }
- }
- #region 短信猫初始信息
- private void selfCheckingTimer_Tick(object sender, EventArgs e)
- {
- //先处理串口接收的数据
- //Console.WriteLine(temp_gsm_str_buf);
- WORK_STATE = 0; //设置工作状态为自检
- string str = "";
- if (temp_gsm_str_buf.Length > 0)
- {
- int CSQ = temp_gsm_str_buf.IndexOf("+CSQ");
- if (CSQ > -1)
- {
- str = temp_gsm_str_buf.Substring(CSQ);
- string[] content = str.Trim().Split(':');
- if (content.Length > 1)
- {
- INF_GSM_CSQ = content[1].ToString().Replace("OK", "").Replace("\r\n", "").Replace("\"", "");
- GSM_RST_ACT++;
- }
- temp_gsm_str_buf = "";
- }
- int CSCS = temp_gsm_str_buf.IndexOf("+CSCS");
- if (CSCS > -1)
- {
- str = temp_gsm_str_buf.Substring(CSCS);
- string[] content = str.Trim().Split(':');
- if (content.Length > 1)
- {
- INF_GSM_CSCS = content[1].ToString().Replace("OK", "").Replace("\r\n", "").Replace("\"", "");
- GSM_RST_ACT++;
- }
- temp_gsm_str_buf = "";
- }
- int CSCA = temp_gsm_str_buf.IndexOf("+CSCA");
- if (CSCA > -1)
- {
- str = temp_gsm_str_buf.Substring(CSCA);
- string[] content = str.Trim().Split(':');
- if (content.Length > 1)
- {
- INF_GSM_CSCA = content[1].ToString().Replace("OK", "").Replace("\r\n", "").Replace("\"", "");
- GSM_RST_ACT++;
- }
- temp_gsm_str_buf = "";
- }
- int CNMI = temp_gsm_str_buf.IndexOf("+CNMI");
- if (CNMI > -1)
- {
- str = temp_gsm_str_buf.Substring(CNMI);
- string[] content = str.Trim().Split(':');
- if (content.Length > 1)
- {
- INF_GSM_CNMI = content[1].ToString().Replace("OK", "").Replace("\r\n", "");
- GSM_RST_ACT++;
- }
- temp_gsm_str_buf = "";
- }
- }
- //
- switch (GSM_RST_ACT)
- {
- case 0: { GsmWrite("ATE0\r"); GSM_RST_ACT++; break; }
- case 1: { GsmWrite("AT+CSQ\r"); break; }
- case 2: { GsmWrite("AT+CMGF=1\r"); GSM_RST_ACT++; break; }
- case 3: { GsmWrite("AT+CSCS?\r"); break; }
- case 4: { GsmWrite("AT+CSCA?\r"); break; }
- case 5: { GsmWrite("AT+CMGF=0\r"); GSM_RST_ACT++; break; }
- //AT+CNMI=0,0,0,0
- //case 6: { GsmWrite("AT+CNMI=3,1,0,0\r"); GSM_ACT++; break; }
- case 6: { GsmWrite("AT+CNMI=0,0,0,0\r"); GSM_RST_ACT++; break; }
- case 7: { GsmWrite("AT+CNMI?\r"); break; }
- case 8:
- {
- INF_GSM_State = true;
- selfCheckingTimer.Enabled = false;
- revTimer.Enabled = true;//启动 接收
- //btnSendTestSM.Enabled = true;
- //checkBox1.Enabled = true;
- //btnSendTestSM.Enabled = true;
- //timer_gsm_REST.Enabled = false;
- break;
- }
- default:
- {
- selfCheckingTimer.Enabled = false;
- //timer_gsm_REST.Enabled = false;
- INF_GSM_State = false;
- MessageBox.Show("短信猫自检失败!");
- break;
- }
- }
- }
- #endregion
- private void GsmWrite(string str)
- {
- Port.Write(str);
- Console.Write(str);
- }
- public int GetSMCount()
- {
- return alRecevingSM.Count;
- }
- }
- }
在短信接收上,上面的代码实现了对垃圾号码、或着非授权号码进行了简单过滤。
另外附上一个机遇W77E58单片机+GPS+GSM做的一个远程定位系统的资料,虽然已经很“老土”了。
http://blog.csdn.net/ex_net/article/details/2765895
可以供大家参考PC机、单片机和GSM的通信处理方式。