市面上的短信猫,大多是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的通信处理方式。