1、负责信息发送的类
2、短信发送线程
3、负责读取信息列表的线程
package com.hgsoft.gsm; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Properties; import javax.comm.CommPortIdentifier; import javax.comm.SerialPort; import javax.comm.SerialPortEvent; import javax.comm.SerialPortEventListener; import javax.comm.UnsupportedCommOperationException; /** * 单例类 * @author chenzebin * */ public class GSMSender implements SerialPortEventListener { private static String portName; //串口号 private static GSMSender msgSender; private String ctrlZ=new String(new char[]{0x1A,0x1D}); //ctrl+z @SuppressWarnings("rawtypes") private Enumeration portList; private CommPortIdentifier portId; private SerialPort serialPort; private OutputStream outputStream; private BufferedReader br; private static String repMsg=""; //每次发出指令后的终端返回的实时信息 private static String lastCommand = ""; private boolean flag = true; private static List<String> msgList = new ArrayList<String>(0);//存放已接收的短信内容 private static List<String> indexList=new ArrayList<String>();//存放被listMsg()方法读过的短信的序号 private String firstStr="0011000D91"; //00占位代表SMSC的位置 //11:基本参数(TP-MTI/VFP) 发送,TP-VP用相对格式 //00:消息基准值(TP-MR) //0D:目标地址数字个数 共13个十进制数(不包括91和‘F’) //91:目标地址格式(TON/NPI) 用国际格式号码(在前面加‘+’) private String middleStr="00088F"; //00:协议标识(TP-PID) 是普通GSM类型,点到点方式 //08 用户信息编码方式(TP-DCS) UCS2编码 //00 有效期(TP-VP) 5分钟 相应的有效期00 to 8F (VP+1)*5 分钟 //90 to A7 12小时+(VP-143)*30分钟 //A8 to C4 (VP-166)*1天 //C5 to FF (VP-192)*1 周 //完整的发送例子如:firstr 手机号码编码 middleStr 短信内容 // 0011000D91 685110489083F6 000800064F 60597D0021 static { String configPath=SendMsgTh.class.getClassLoader().getResource("").getPath(); String pathTemp = configPath.substring(0,configPath.length()-1); configPath = pathTemp.substring(0,pathTemp.lastIndexOf("/")+1); Properties pp=System.getProperties(); try { pp.load(new FileInputStream(new File(configPath+"gsm/config.proerties"))); String temp=null; if((temp=pp.getProperty("portName"))!=null) portName=temp; else portName="COM1"; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static GSMSender getInstance(){ if(msgSender==null)msgSender=new GSMSender(portName); return msgSender; } private GSMSender(String portName){ init(portName); } public void init(String portName) { portList = CommPortIdentifier.getPortIdentifiers(); while (portList.hasMoreElements()) { portId = (CommPortIdentifier) portList.nextElement(); if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { if (portId.getName().equals(portName)) { // if (portId.getName().equals("/dev/term/a")) { try { serialPort = (SerialPort) portId.open("SimpleWriteApp", 2000); serialPort.addEventListener(this); serialPort.notifyOnDataAvailable(true); } catch (Exception e) { e.printStackTrace(); } try { outputStream = serialPort.getOutputStream(); InputStream inputStream = serialPort.getInputStream(); br = new BufferedReader(new InputStreamReader( inputStream)); } catch (IOException e) { e.printStackTrace(); } try { serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); } catch (UnsupportedCommOperationException e) { e.printStackTrace(); } } } } } /** * * @param line * @param isBlock 发送信息后是否阻塞 * @throws InterruptedException */ public synchronized void sendMsg(String line,boolean isBlock) throws InterruptedException{ System.out.println("send:"+line); try { Thread.sleep(1000); outputStream.write(line.getBytes()); outputStream.flush(); if(isBlock) this.wait(); } catch (IOException e) { e.printStackTrace(); } } @SuppressWarnings("unused") private String readReturnInfo(){ String str=null; try { while((str=br.readLine())!=null){ System.out.println("rece: "+str); if(str.equals("OK")||str.equals("ERROR"))break; } } catch (IOException e) { e.printStackTrace(); } return str; } /** * 调用此函数发作息 * @param context 发送的内容 * @param tele 改送的电话 * @throws InterruptedException */ public void sendMsg(String tele,String context) throws InterruptedException{ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //完整的短信编码为:0011000D91 685110489083F6 000800 064F60597D0021 String msg=firstStr+EncodeTele(tele)+middleStr+parseContext(context); int msgNum=(msg.length()-2)/2;//去掉最前面的两人个补位符 //sendMsg("AT+CMGF=0\r\n"); setLastCommand("AT+CMGS="); sendMsg("AT+CMGS="+msgNum+"\r\n",false); sendMsg(msg,false); sendMsg(ctrlZ,true);//加入此句完成发送,测试时不要加入,用回车换行代替 //sendMsg(msg+"\r\n",false); //测试行,让短信发送失败 } /** * 将十进行制转成二位显示的十六进制 * @param i * @return */ private String changeHexStr(int i){ String temp="00"+Integer.toHexString(i); return temp.substring(temp.length()-2,temp.length()); } /** * 对手机号码进行编码 * @param tele * @return */ private String EncodeTele(String tele) { if (!(tele.substring(0, 2).equals("86"))) { tele = "86" + tele; // 检查当前接收手机号是否按标准格式书写,不是,就补上“86” } char [] chars=tele.toCharArray(); if(chars.length%2!=0){ for(int i=0;i<chars.length-1;i+=2){ if(chars[i]==chars[i+1])continue; swap(chars,i,i+1); } Character c=chars[chars.length-1]; chars[chars.length-1]='F'; return new String(chars).concat(c.toString()); }else { for(int i=0;i<chars.length;i+=2){ if(chars[i]==chars[i+1])continue; swap(chars,i,i+1); } return new String(chars); } } private String parseContext(String context) { String temp=EncodeUCS2(context); String numString=changeHexStr(temp.length()/2); return numString+temp; } private void swap(char chars[],int i,int j){ char temp=chars[i]; chars[i]=chars[j]; chars[j]=temp; } /** * UCS2解码 * * @param src * UCS2 源串 * @return 解码后的UTF-16BE字符串 */ private String DecodeUCS2(String src) { byte[] bytes = new byte[src.length() / 2]; for (int i = 0; i < src.length(); i += 2) { bytes[i / 2] = (byte) (Integer .parseInt(src.substring(i, i + 2), 16)); } String reValue = ""; try { reValue = new String(bytes, "UTF-16BE"); } catch (Exception e) { e.printStackTrace(); } return reValue; } /** * UCS2编码 * * @param src * UTF-16BE编码的源串 * @return 编码后的UCS2串 */ private String EncodeUCS2(String src) { byte[] bytes = null; try { bytes = src.getBytes("UTF-16BE"); } catch (Exception e) { e.printStackTrace(); } StringBuffer reValue = new StringBuffer(); StringBuffer tem = new StringBuffer(); for (int i = 0; i < bytes.length; i++) { tem.delete(0, tem.length()); tem.append(Integer.toHexString(bytes[i] & 0xFF)); if(tem.length()==1){ tem.insert(0, '0'); } reValue.append(tem); } return reValue.toString().toUpperCase(); } /** * 7bit解码 */ private static String decode7bit(String src) { String result = null; int[] b; String temp = null; byte srcAscii; byte left = 0; if (src != null && src.length() % 2 == 0) { result = ""; b = new int[src.length() / 2]; temp = src + "0"; for (int i = 0, j = 0, k = 0; i < temp.length() - 2; i += 2, j++) { b[j] = Integer.parseInt(temp.substring(i, i + 2), 16); k = j % 7; srcAscii = (byte) (((b[j] << k) & 0x7F) | left); result += (char) srcAscii; left = (byte) (b[j] >>> (7 - k)); if (k == 6) { result += (char) left; left = 0; } if (j == src.length() / 2) result += (char) left; } } return result; } public List<String> listMsg() throws InterruptedException{ sendMsg("AT+CMGF=0\r\n",true); setLastCommand("AT+CMGL=4"); sendMsg("AT+CMGL=4\r\n",true); return msgList; } /** * 对短信进行解码 * @param oneMsg * @return */ public String parseMsg(String oneMsg) { String str=oneMsg.substring(20,22); //05说明发送电话号码只有5位数,信息内容从50位开始 //默认按13位电话号码截取,信息内容从58位开始 if(str.equals("05")){ if("00".equals(oneMsg.substring(32,34))) return DecodeTime(oneMsg.substring(34,46))+","+DecodeTele(oneMsg.substring(24, 30))+decode7bit(oneMsg.substring(50,oneMsg.length())); else return DecodeTime(oneMsg.substring(34,46))+","+DecodeTele(oneMsg.substring(24, 30))+DecodeUCS2(oneMsg.substring(50,oneMsg.length())); } else{ if("00".equals(oneMsg.substring(40,42))) return DecodeTime(oneMsg.substring(42,54))+","+DecodeTele(oneMsg.substring(24,38))+decode7bit(oneMsg.substring(58,oneMsg.length())); else return DecodeTime(oneMsg.substring(42,54))+","+DecodeTele(oneMsg.substring(24,38))+DecodeUCS2(oneMsg.substring(58,oneMsg.length())); } } /** * 解析日期 * @param date 含十二位字符的字符串 * @return */ public String DecodeTime(String time){ char [] chars=time.toCharArray(); for(int i=0;i<chars.length;i+=2){ if(chars[i]==chars[i+1])continue; swap(chars,i,i+1); } String temp = new String(chars); String newTime="20"+temp.substring(0,2)+"-"+temp.substring(2,4)+"-"+temp.substring(4,6)+" "+temp.substring(6,8) +":"+temp.substring(8,10)+":"+temp.substring(10,12); return newTime; } /** * 对手机号码进行解码 * @param tele * @return */ public String DecodeTele(String tele) { //找出倒数第二位 char c=tele.charAt(tele.length()-2); char [] chars=tele.toCharArray(); for(int i=0;i<chars.length;i+=2){ if(chars[i]==chars[i+1])continue; swap(chars,i,i+1); } if(c=='F'){//判断倒数第二位是否是F,如果是将','号放在数组最后一位,不是则加在字符串最后 chars[chars.length-1]=','; return new String(chars); } else return new String(chars)+","; } /** * 删除被listMsg()方法读过的短信 * @throws InterruptedException */ public void deleteMsg() throws InterruptedException{ for(String index:indexList){ sendMsg("AT+CMGD="+index+"\r\n",true); } indexList.clear();//清空列表数据 } public String getRepMsg(){ return repMsg; } public List<String> getMsgList(){ return msgList; } @SuppressWarnings("static-access") @Override /** * 串口监听函数 */ public synchronized void serialEvent(SerialPortEvent ev) { System.out.println("exec event"); flag = true; while (flag) { if (ev.getEventType() == ev.DATA_AVAILABLE){ try { if (lastCommand.equals("AT+CMGL=4")) {//AT+CMGL=4 列出所有短信 readrepMsg(); // 以0891开头说明是信息 if (repMsg.startsWith("0891")) {//089168310820000... msgList.add(parseMsg(repMsg).trim()); }else if(repMsg.startsWith("+CMGL")){//+CMGL: 2,1,,26 2为短信序号 indexList.add(repMsg.substring(7,8)); } else outEvent(); } else { readrepMsg(); outEvent(); } } catch (Exception e) { e.printStackTrace(); } } } this.notify(); } private void readrepMsg(){ String s; try { s = br.readLine(); repMsg = s == null ? "" : s; System.out.println("response is:" + repMsg); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void outEvent(){ if(repMsg.equals("OK")||repMsg.equals("ERROR")){ setLastCommand(""); flag =false; } } private void setLastCommand(String command){ lastCommand = command; } public void destroy(){ try { if(outputStream!=null) outputStream.close(); if(br!=null) br.close(); if(serialPort!=null) serialPort.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { //char bufs[] = { 0x1A, 0x1D }; //String s2 = new String(bufs); //MSGSender cd = new MSGSender("COM1"); //GSMSender cd=GSMSender.getInstance(); // text发送方式 // cd.sendMsg("AT+CMGF=1\r\n"); // cd.sendMsg("AT+CMGS=15018409386\r\n"); // cd.sendMsg("hello,World!11"); // //cd.sendMsg(s2);//ctrl+z // cd.readMsg(); // pdu发送方式 // cd.sendMsg("AT+CMGF=0\r\n"); // cd.sendMsg("AT+CMGS=21\r\n"); // cd.sendMsg("0011000D91685110489083F6000800064F60597D0021"); // cd.sendMsg(s2); // cd.readMsg(); // // at+cmgl=4,列出所有信息 // cd.sendMsg("AT+CMGF=0\r\n"); // cd.sendMsg("AT+CMGL=4\r\n"); // cd.readMsg(); //测试 // cd.sendMsg("8615018409386","你好!"); // cd.sendMsg("AT+CMGF=0\r\n"); // List<String> list=new ArrayList<String>(); // list.add("8615018409386,你中奖了!"); // list.add("8615018409386,今晚有美食吃!"); // for(int i=0;i<list.size();i++){ // System.out.println("index:"+i); // String[] strs=list.get(i).split(","); // cd.sendMsg(strs[0], strs[1]); // while(true){ // if(cd.repMsg.equals("OK")||cd.repMsg.equals("ERROR"))break; // else { // try { // Thread.sleep(20000); // break; // } catch (InterruptedException e) { // e.printStackTrace(); // } // } // } // } // // long s=System.currentTimeMillis(); // List list=cd.listMsg(); // System.out.println(list.size()); // System.out.println(System.currentTimeMillis()-s); //测试短信删除 // cd.deleteAllMsg(); } }
2、短信发送线程
package com.hgsoft.gsm; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; /** * 此线程负责发送短信 * @author chenzebin * */ public class SendMsgTh extends Thread{ private GSMSender msgSender; private static File toSendFile; //准备发送信息的文件 private static File sendingFile ;//正在发送信息的文件 private static File sendNotSuccessFile ;//储存发送未成功的信息的文件 private static File historyFile ;//储存发送成功的信息的文件 //private Thread receTh = new Thread(new ReceMsgTh(msgSender));//读取短信列表线程 private static PrintWriter historyPw; private static PrintWriter sendNotSuccessPw; private static DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private BufferedReader br; private boolean flag = true; /** * @param args */ public static void main(String[] args) { Thread MsgTh=new SendMsgTh(null); MsgTh.start(); } public SendMsgTh(GSMSender msgSender){ if(msgSender==null)this.msgSender=GSMSender.getInstance(); else this.msgSender=msgSender; init(); } private void init(){ String configPath=SendMsgTh.class.getClassLoader().getResource("").getPath(); String pathTemp = configPath.substring(0,configPath.length()-1); configPath = pathTemp.substring(0,pathTemp.lastIndexOf("/")+1); File configFile=new File(configPath+"gsm/config.proerties"); boolean flag=true; while(flag){//直到找到config.properties,程序才继续执行 if(configFile.exists()){ try { toSendFile = new File(configPath + "gsm/ready.txt"); // 准备发送信息的文件 sendingFile = new File(configPath + "gsm/sending.txt");// 正在发送信息的文件 sendNotSuccessFile = new File(configPath + "gsm/fail.txt");// 储存发送未成功的信息的文件 historyFile = new File(configPath + "gsm/success.txt");// 储存发送成功的信息的文件 historyPw=new PrintWriter(new FileWriter(historyFile,true)); sendNotSuccessPw=new PrintWriter(new FileWriter(sendNotSuccessFile,true)); flag=false; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } else { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("找不到配置文件:"+configPath+"config.properties!"); } } } @Override public void run() { try { //receTh.start();//启动读取信息表列线程 //boolean flag = true; while (flag) { System.out.println("search ready.txt......."); Thread.sleep(10000);//线程每间隔10秒重新扫描toSend.txt文件 synchronized(msgSender){//利用静态变量与接收线程进行同步 if(sendingFile.exists()){//判断是否存在sending.txt,是的话重发 //一般是因为短信未发完非法关闭系统会出现此情况) sendingMsgs(); } else if (toSendFile.exists()) {//判断toSend.txt是否存在 if(toSendFile.renameTo(sendingFile)){//改名成功,则发送短信 sendingMsgs(); } } else//不存在则继续扫描 continue; } } } catch (InterruptedException e) { //System.out.println("等待发送文件............"); //e.printStackTrace(); System.out.println("信息发送线程中断....."); flag = false; } } /** * 将文件中的信息逐行读出发送 * @throws InterruptedException */ private void sendingMsgs() throws InterruptedException{ msgSender.sendMsg("AT+CMGF=0\r\n",true); try{ br=new BufferedReader(new InputStreamReader(new FileInputStream(sendingFile))); String msg=null; while((msg=br.readLine())!=null&&!msg.trim().equals("")){ boolean flag=true; String [] strs=parseLine(msg); String tele=strs[0]; String context=strs[1]; while(flag){ if((!"".equals(tele))&&(!"".equals(context))){ //发送信息 if(context.length()<=70){//判断是否小于70个字符,是则直接改送 sendMsg(tele,context); flag=false; } else {//否则先发送前面70个字符 sendMsg(tele,context.substring(0,70)); context=context.substring(70,context.length()); } } } } if(br!=null) br.close();//关闭sending.tx的流,之后将文件delete sendingFile.delete(); }catch (Exception e){ e.printStackTrace(); } } /** * 解析从文件中读取的短信内容 * @param msg 短信想关信息 信息格式为:8613888264553,你中奖了,赶快去拿奖吧! * @return 字符串数组 strs strs[0]:手机号码 , strs[1]:短信内容 */ private String[] parseLine(String msg) { String[] strs=new String[]{"",""}; try{ int index=msg.indexOf(","); strs[0]=msg.substring(0,index); strs[1]=msg.substring(index+1,msg.length()); }catch (Exception e){ return strs; } return strs; } /** * 发送单条短信 * @param tele 电话号码 * @param context 信息内容 * @throws InterruptedException */ private void sendMsg(String tele,String context) throws InterruptedException{ Date date=new Date(); msgSender.sendMsg(tele, context);//此方法返回时说明,已经成功获取串口返回信息 //终端返回OK或ERROR才继续发下条信息 if(msgSender.getRepMsg().equals("OK")){//发送成功则写入history.txt historyPw.println(tele+","+context); historyPw.flush(); System.out.println(sdf.format(date)+","+tele+","+context+"发送成功!"); } else if(msgSender.getRepMsg().equals("ERROR")){//发送失败则写入sendNotSuccess.txt sendNotSuccessPw.println(tele+","+context); sendNotSuccessPw.flush(); System.out.println(sdf.format(date)+","+tele+","+context+"发送失败!"); } try { Thread.sleep(3000);//等3秒发下条短信 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void destroy(){ try{ this.interrupt(); if(historyPw!=null)historyPw.close(); if(sendNotSuccessPw!=null)sendNotSuccessPw.close(); if(br!=null)br.close(); }catch(Exception e){ e.printStackTrace(); } } }
3、负责读取信息列表的线程
package com.hgsoft.gsm; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Properties; /** * 此线程类为自动读取信息列表,默认为半时读取一次 * @author chenzebin * */ public class ReceMsgTh extends Thread{ //private static String path; private GSMSender msgSender; private static File receMsgFile; private static File tempFile; private PrintWriter pw; private List<String> msgList=new ArrayList<String>(); private static long sleepTime; private boolean flag=true; public static void main(String args[]){ Thread th=new ReceMsgTh(null); th.start(); } public ReceMsgTh(GSMSender msgSender){ if(msgSender==null)this.msgSender=GSMSender.getInstance(); else this.msgSender=msgSender; init(); } private void init(){ String configPath=SendMsgTh.class.getClassLoader().getResource("").getPath(); String pathTemp = configPath.substring(0,configPath.length()-1); configPath = pathTemp.substring(0,pathTemp.lastIndexOf("/")+1); Properties pp=System.getProperties(); try { pp.load(new FileInputStream(new File(configPath+"gsm/config.proerties"))); String temp=null; if((temp=pp.getProperty("receThSleepTime"))!=null){ sleepTime=Long.parseLong(temp+"000"); } else sleepTime=1800000; receMsgFile=new File(configPath+"gsm/received.txt"); tempFile=new File(configPath+"gsm/temp.txt"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { while(flag){ try { synchronized(msgSender){ if(receMsgFile.exists()){ if(receMsgFile.renameTo(tempFile)) receMsg(true); } else receMsg(false); msgSender.deleteMsg();//将读过的短信删除 } Thread.sleep(sleepTime); } catch (InterruptedException e) { //e.printStackTrace(); System.out.println("信息接收线程中断......."); flag = false; } } } /** * 将信息列表写入文件 * @param flag 文件是否追加 * @throws InterruptedException */ public void receMsg(boolean flag) throws InterruptedException{ try{ pw=new PrintWriter(new OutputStreamWriter(new FileOutputStream(tempFile,flag))); msgList=msgSender.listMsg(); for (int i = 0; i < msgList.size(); i++) { pw.println(msgList.get(i)); pw.flush(); } msgList.clear();//读完信息要信息列表中的信息清空,防止下次重复出现已读过的信息 pw.close(); tempFile.renameTo(receMsgFile); System.out.println("列表读取完毕"); }catch(IOException e){ e.printStackTrace(); } } @Override public void destroy(){ this.interrupt(); if(pw!=null)pw.close(); } }
相关推荐
关于Wavecom短信猫的PDU短信息发送案例
用短信猫做的支持PDU短信发送和接收的编码
pdu模式发送短信
用C语言设计PDU正常短信以及长短信的编码解码
//手机号码转换为pdu模式 private string telc(string MobileNum) { int tl; string ltem, rtem, ttem; int ti; ttem = ""; tl = MobileNum.Trim().Length; if (tl != 11 && tl != 13) { MessageBox.Show...
最近在做短信模块,自己做了个测试实例,测试成功。中间因为回车、多个少个字符的问题走了很多弯路。把成功上传上来,供大家参考,研究。delphi 7版本。需要安装spcom控件
采用pdu模式发送短信的源代码,支持GSM短信猫
短信格式的编码主要是有两种一个是 text 格式的,明文不用解码一看就懂, 一个是 PDU 格式的, 国内的都是的 PDU 格式的。 以下分析的都是 PDU 格式数据包。 2.PDU 格式的数据包,发送的数据包和...
pdu_发送短信的例子参考,对于使用at指令发送短信的用户可以下载看看
完美实现GSM模块发送中文PDU格式短信,包含常用字库
pdu格式发送短信息例子集首先将短信息格式设为PDU模式 :AT+CMGF=0
初学者开发GSM模块、4G模块的时候,需要将国标字符转换成unicode码发送到模块中,才能实现中文短信的收发。该软件可以下显示转码后PDU包的格式,也可以进行PDU包的解码,很适合调试过程中的朋友。
GSM_8转7bit ,GSM_7转8bit,PDU_收短信, PDU_发短信 PDU发送接收编解码.zip
PDU格式发送短信读取短信中源手机号码、目的手机号码格式转换函数
一个用单片机控制GSM模块发送短信的程序,使用的是PDU格式。
西门子模块命令集应用与PDU短信息格式及说明 SMS短息格式.pdf
针对SIM800C通讯模块,如何向10086号码发短信,及PDU编码的详细规则进行了梳理,形成该学习实践笔记。