起因
曾经用过西门子出的短信猫, 好处是直接有sdk开发包, 不会硬件开发也能直接使用
缺点也是明显的, 就是只支持windows系统, 另外就是在windows下工作很不稳定, 隔开几天就会出现收不到短信的毛病, 要断电重启设备才有机会恢复(还不是必然恢复)
后来在地府(dfrobot)发现了新品"gravity: uart a6 gsm & gprs 无线通信模块",买来试了一下发现可用(不过不清楚地府的a6和外面常见的sim800系列、sim900系列有什么不同), 而且可以自己写驱动支持linux下运行,完美
期间也碰到一些小坑, 记录一下。
需求清单
- 自动初始化gprs模块
- 接收短信并能解析出重要元素(包括:来电号码/时间/短信内容)
- 把解析到的短信内容上传到服务器保存
- 清除已阅短信
- 支持linux系统
- 开发语言:java
硬件清单
- 树莓派2代b型(3代串口使用上有区别,需要另外的方法处理)
- gravity: uart a6 gsm & gprs 无线通信模块
- usb无线网卡(可选)
- usb电源适配器2个(重要,为什么要2个后面会说明)
- 16gb tf卡一张
- 可以接收短信的手机卡1张(必须是移动或联通的卡, 电信的不支持)
接线方法
(树莓派40pin引脚图)
(a6引脚说明)
树莓派 a6
--------------------------------------
gpio15 rx
gpio16 tx
gnd gnd
重要: a6模块的电源需要单独供电!!!
a6模块不能直接从树莓派上的gpio 5v针脚接电,因为电流不足!
最开始的时候, 我是从树莓派上取电供给a6, 结果串口怎么都无法通信,刚开始还以为是波特率的问题,结果折腾了半天后, 留意到a6上有个蓝灯(上面写着sleep)有明暗变化, 不稳定,感觉像是电压不稳定一样, 果断试了一下把a6外接电源,然后a6才正常工作! 可以从蓝灯看得出来,亮度较高,且稳定(不闪烁)
资料准备
- 树莓派系统(用noobs或raspbian都可以)
- pi4j (java支持包)
- at指令知识
树莓派系统安装方法可以自行搜索,或看我之前发过的文章
系统装好后还涉及到如何把gpio15(tx)和gpio16(rx)启用的问题, 见这篇文章:《两个树莓派通过串口通信》
pi4j是个能让java访问树莓派40个gpio的支持包, 可以上官网下载安装,传送门>>>
最重要和容易掉坑的是关于接收短信的at指令部分,下面要详细讲解
这里需要做的功能是利用a6接收短信,涉及到以下指令
* 第一步:初始化gprs.模块
* at 握手 / sim卡检测等
* at+cpin? 查询是否检测到sim卡
* at+csq 信号质量测试,值为0-31,31表示最好
* at+ccid 读取sim的ccid(sim卡背面20位数字),可以检测是否有sim卡或者是否接触良好
* at+creg? 检测是否注册网络
------以上指令用于初始化模块,一般接线没问题,波特率设置没问题的话都是比较容易调通
* 第二步:初始化gprs.设置短信模式及短信接收参数
* at+cmgf=1 0-pdu, 1-文本格式
* at+csdh=1
* at+cpms="sm","sm","sm" 将信息保存在sim卡中, sm-表示存在sim卡上
* at+cnmi=2,1,0,1,1 收接通知,并存在指定位置(与at+cpms设置有关)
极易掉坑系列,逐个讲解:
设置短信格式指令:at+cmgf=1
分2种短信格式: 0-pdu, 1-文本格式
如果设置的是pdu模式, 那么你收到的短信就是这样 的:
+ciev: "message",1
+cmt: ,32
0891683110200005f0040ba18126601728f00000710102610272230e74747a0e4acf416110bd3ca703
如何读懂pdu要另外翻阅专业文章, 这里如果你不使用发短信功能的话,建议不要采用pdu格式
pdu格式的好处是可以发中文短信!
好, 如果设置是文本格式,收到的短信就类似下面这样的:
+ciev: "message",1
+cmt: "test again ,中文也试试
直接就能看到短信内容,中文也一样可以显示出来(注意它可以gb2312或gbk编码)
然后就有问题产生了, 新短信来时是上面这样的格式, 短信内容是可以获取了, 但特么为什么看不出是谁(手机号)发来的呢?下面就是入坑的时候:
看不到手机号怎么办, 你可以试试这个指令:
at+cmgl="all"
它能读出存在sim卡上的短信(包括已读和未读,以及外发时存着的短信),执行后收到的内容大概是这个样子:
+cmgl: 2,"rec read","106907931100",,"2017/06/02,10:15:21+08",160,134
【小米】[小米移动]您2017年5月共消费0.75元,当前余额99.05元。其中:数据流量费0.05元;语音通信费0.7元;短/彩信费+cmgl: 3,"rec read","106907931100",,"2017/07/02,10:15:19+08",160,54
。查询账单 http://10046.mi.com。+cmgl: 4,"rec read","106907931100",,"2017/07/02,10:15:19+08",160,134
【小米】[小米移动]您2017年6月共消费1.32元,当前余额97.88元。其中:数据流量费1.32元;语音通信费0元;短/彩信费0元+cmgl: 5,"rec read","106908761100",,"2017/08/02,10:15:30+08",160,54
。查询账单 http://10046.mi.com。+cmgl: 6,"rec read","106908761100",,"2017/08/02,10:15:30+08",160,134
【小米】[小米移动]您2017年7月共消费0.81元,当前余额97.01元。其中:数据流量费0.81元;语音通信费0元;短/彩信费0元at+cmgd=2
好! 很明显你要的信息都有了, 来电号码/时间/短信内容, 似乎可以用了喔!
但是这时你会发现刚收到的信息不一定在这个清单里! 这是怎么回事呢? 我反正查阅了很多资料,费了大量的时间也不知怎么回事
后来才发现这个指令(查看sim卡内存情况):
at+cpms?
执行后你可能会看到这个结果:
+cpms: "mt",0,50,"sm",1,50,"me",0,50
ok
重点关注"sm"后面第1个数字"1"代表当前存了多少条短信, 第2个数字"50"代表存储上限
"sm"表示sim卡, 其它2个一个代表手机设备, 另一个是手机内存
然后你给a6发个新短信, 有可能发现这个"1"不会增加! 为什么收到的新短信不存到sim卡上呢?
然后就找到这个指令
at+cnmi=<mode>,<mt>,<bm>,<ds>,<bfr>
这个指令比较复杂, 它负责设置收到新短信后的处理机制, 下面是参数含义
<mode>控制通知te的方式.
0 - 先将通知缓存起来,再按照<mt>的值进行发送.
1 - 在数据线空闲的情况下,通知te,否则,不通知te.
2 - 数据线空闲时,直接通知te;否则先将通知缓存起来,待数据线空闲时再行发送.
3 - 直接通知te.在数据线被占用的情况下,通知te的消息将混合在数据中一起传输.
<mt>设置短消息存储和通知te的内容.
0 - 接受的短消息存储到默认的内存位置(包括class 3),不通知te.
1 - 接收的短消息储存到默认的内存位置,并且向te发出通知(包括class 3).通知的形式为:+cmti:"sm",<index>
2 - 对于class 2短消息,储存到sim卡,并且向te发出通知;对于其他class,直接将短消息转发到te:+cmt:[<alpha>],<length><cr><lf><pdu>(pdu模式)
或者+cmt:<oa>,[<alpha>,]<scts>[,<tooa>,<fo>,<pid>,<dcs>,<sca>,<tosca>,<length>]<cr><lf><data>(text模式)
3 - 对于class 3短消息,直接转发到te,同<mt>=2;对于其他class,同<mt>=1.
<bm>设置小区广播
0 - 小区广播不通知
2 - 新的小区广播通知,返回+cbm:;length;;cr;;lf;;pdu;
3 - class3格式的小区广播通知,使用bm=2格式
<ds>状态报告
0 - 状态报告不通知
1 - 新的状态报告通知,返回:+cds:;length;;cr;;lf;;pdu;
2 - 如果新的状态报告存储到me,则返回:+cdsi:;mem;,;index;
相信看完你已经蒙圈, 我就是, 如果你看得懂, 那恭喜了!
我在这里采用的参数是
at+cnmi=2,1,0,1,1 收接通知,并存在指定位置
这时再测试一下发条新短信给a6
+ciev: "message",1
+cmti: "sm",0
现在不显示短信内容了(反正显示也没用, 因为没来电号码), 但"sm"后面仍然是0!
这时再用at+cmgl="all" 你会发现短信依然没存到卡上, 结果当然也没法看到短信内容及来电号码等信息啦
这是怎么回事裂
后来发现这个at+cnmi跟刚才说的指令(at+cpms)息息相关,再来查一下:
at+cpms?
+cpms: "mt",0,50,"sm",1,50,"me",0,50
ok
注意看, 如果你看到的和上面差不多, 会发现有"mt"和"me"存在, 这时收到短信虽然在cnmi告诉a6收到短信要存下来啊! 但是a6找不到"mt"和"me",结果存储失败!
我们现在是希望它收到短信后能存在sim卡上, 所以要设置一下:
at+cpms="sm","sm","sm"
再发条短信试试效果:
+ciev: "message",1
+cmti: "sm",1
现在看到"sm"后面是1了! 后面这个1表示的是短信存储的sim卡内存的位置
然后可以用指令查看短信内容了, 这里有2种方法
方法1,单条读取(at+cmgr=index)
at+cmgr=1
+cmgr: "rec unread","18620671820",,"2017/10/26,11:37:03+08",161,17,0,0,"+8613010200500",145,25
test again ,中文也试试
方法2,全部读取(at+cmgl="all")
+cmgl: 2,"rec read","106907931100",,"2017/06/02,10:15:21+08",160,134
【小米】[小米移动]您2017年5月共消费0.75元,当前余额99.05元。其中:数据流量费0.05元;语音通信费0.7元;短/彩信费+cmgl: 3,"rec read","106907931100",,"2017/07/02,10:15:19+08",160,54
。查询账单http://10046.mi.com 。+cmgl: 4,"rec read","106907931100",,"2017/07/02,10:15:19+08",160,134
【小米】[小米移动]您2017年6月共消费1.32元,当前余额97.88元。其中:数据流量费1.32元;语音通信费0元;短/彩信费0元+cmgl: 5,"rec read","106908761100",,"2017/08/02,10:15:30+08",160,54
。查询账单 http://10046.mi.com。+cmgl: 6,"rec read","106908761100",,"2017/08/02,10:15:30+08",160,134
【小米】[小米移动]您2017年7月共消费0.81元,当前余额97.01元。其中:数据流量费0.81元;语音通信费0元;短/彩信费0元at+cmgd=2
小结一下正确获取短信的姿势(流程):
at+cmgf=1
at+csdh=1
at+cpms="sm","sm","sm"
at+cnmi=2,1,0,1,1
ps: 其中有一条指令没解释:at+csdh=1
这个留给大家查资料
好, 接下来只需要写出java代码分析短信内容即可.
程序部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
|
import java.io.ioexception; import java.util.date; import com.common.datetimeutil; import com.common.stringutil; import com.pi4j.io.serial.baud; import com.pi4j.io.serial.databits; import com.pi4j.io.serial.flowcontrol; import com.pi4j.io.serial.parity; import com.pi4j.io.serial.serial; import com.pi4j.io.serial.serialconfig; import com.pi4j.io.serial.serialfactory; import com.pi4j.io.serial.serialport; import com.pi4j.io.serial.stopbits; import com.pi4j.util.commandargumentparser; import com.pi4j.util.console; /** * this example code demonstrates how to perform serial communications using the raspberry pi. * * @author robert savage */ public class seriallistensms { /** * this example program supports the following optional command arguments/options: * "--device (device-path)" [default: /dev/ttyama0] * "--baud (baud-rate)" [default: 38400] * "--data-bits (5|6|7|8)" [default: 8] * "--parity (none|odd|even)" [default: none] * "--stop-bits (1|2)" [default: 1] * "--flow-control (none|hardware|software)" [default: none] * * @param args * @throws interruptedexception * @throws ioexception */ public static void main(string args[]) throws interruptedexception, ioexception { // !! attention !! // by default, the serial port is configured as a console port // for interacting with the linux os shell. if you want to use // the serial port in a software program, you must disable the // os from using this port. // // please see this blog article for instructions on how to disable // the os console for this port: // https://www.cube-controls.com/2015/11/02/disable-serial-port-terminal-output-on-raspbian/ // create pi4j console wrapper/helper // (this is a utility class to abstract some of the boilerplate code) final console console = new console(); // print program title/header console.title( "<-- the pi4j project -->" , "监听串口(gpio15-tx / gpio16-rx)数据并写入memcached中" ); // allow for user to exit program using ctrl-c console.promptforexit(); // create an instance of the serial communications class final serial serial = serialfactory.createinstance(); byte [] data = new byte [ 1024 ]; //数据缓冲区 try { // create serial config object serialconfig config = new serialconfig(); system.out.println( ">>>" +serialport.getdefaultport()); config.device(serialport.getdefaultport()) // "/dev/ttyacm0" .baud(baud._115200) .databits(databits._8) .parity(parity.none) .stopbits(stopbits._1) .flowcontrol(flowcontrol.none); // parse optional command argument options to override the default serial settings. if (args.length > 0 ){ config = commandargumentparser.getserialconfig(config, args); } // display connection details console.box( " connecting to: " + config.tostring(), " data received on serial port will be displayed below." ); // open the default serial device/port with the configuration settings serial.open(config); serial.flush(); system.out.println( "serial.isopen():" +serial.isopen()); /**初始化gprs模块**/ boolean isinit = initgprs(serial); long trydelay = 2000 ; while (!isinit){ system.out.println( "初始化gprs模块不成功, 请检查模块工作状态灯, 以及sim卡是否接触良好..." +trydelay); thread.sleep(trydelay+= 1000 ); isinit = initgprs(serial); if (trydelay>( 10 * 1000 )){ return ;} //检测10次都不成功时, 退出程序 } /**初始化短信参数**/ isinit = initgprs_sms(serial); trydelay = 2000 ; while (!isinit){ system.out.println( "初始化短信参数不成功, 请检查模块工作状态灯, 以及sim卡是否接触良好." ); thread.sleep(trydelay+= 1000 ); isinit = initgprs_sms(serial); if (trydelay>( 10 * 1000 )){ return ;} //检测10次都不成功时, 退出程序 } //每次开机时尝试读取一次存储卡中的短信 string res = new string(sendcmd(serial, "at+cmgl=\"all\"" ), "gbk" ); system.out.println( "at+cmgl=\"rec read\".res:" +res); if (res.indexof( "ok" )==- 1 ){ system.out.println( "设置失败!" ); } //下面进入主程序 system.out.println( "进入短信监听程序:" ); long old_msg_delay = 60000 ; //设置旧短信搜索间隔时间(毫秒),在sim卡内存中搜索数据 long old_msg_count = 0 ; //旧短信计时器 int index = 1 ; data = null ; while ( true ){ system.out.print( "." ); if (!serial.isopen()){ system.out.println( "串口未打开, 退出程序" ); break ; } if (old_msg_count>=old_msg_delay){ // system.out.println( "发送获取sim卡内存中的所有信息的指令" ); sendcmd(serial, "at+cmgl=\"all\"" ); old_msg_count = 0 ; } else { old_msg_count+= 1000 ; //system.out.println("old_msg_count..."+old_msg_count); } if (serial.available()> 0 ){ while (serial.available()> 0 ){ data=serial.read(); //此处接收到的数据上限是1024 //system.out.print(new string(serial.read(), "utf-8")); } serial.flush(); } if (data!= null ){ //接收到数据 string cc = new string(data, "gbk" ); //处理中文 system.out.println( "cc:" +cc); if (cc!= null && !cc.trim().equals( "" )){ //处理数据 /** * 有新短信时: * +ciev: "message",1 * * +cmti: "sm",1 */ if (cc.indexof( "+cmti" )!=- 1 ){ index = getindexfromnewsms(cc); system.out.println( "发现新短信.index:" +index); sendcmd(serial, "at+cmgr=" +index); } if (cc.indexof( "+cmgr" )!=- 1 ){ string[] contents = getcontentfromindex(index, cc); system.out.println( "[at+cmgr=index]读取存在卡上的短信内容.分析后:" ); if (contents!= null ){ system.out.println( "新短信内容:" ); for (string tt : contents){ system.out.println(tt); } //保存读到的短信 -> 服务器 if (senddatatoserver(contents)){ //删除已读出的短信 system.out.println( "删除已读出的新短信.index:" +contents[ 0 ]); delsmsbyindex(serial, integer.parseint(contents[ 0 ])); } } else { system.out.println( "新短信内容:null" ); } } /** * 查询旧短信时: * at+cmgl="all" * * +cmgl: 1,"rec read","18620671820",,"2017/10/26,11:37:03+08",161,25 * just because the people11 * +cmgl: 2,"rec read","18620671820",,"2017/10/26,11:37:03+08",161,25 * just because the people11 */ if (cc.indexof( "cmgl:" )!=- 1 ){ //获取第1条短信 string[] contents = getcontentfromstoragesms(cc); system.out.println( "[at+cmgl=\"all\"]存在卡上的短信内容.分析后:" ); for (string tt : contents){ system.out.println(tt); } //保存读到的短信 if (senddatatoserver(contents)){ //删除已读出的短信 system.out.println( "删除已读出的旧短信.index:" +contents[ 0 ]); delsmsbyindex(serial, integer.parseint(contents[ 0 ])); } } } else { system.out.println( "data:" + new string(data)); system.out.println( "data(byte[]) 转换成 string时出错" ); } } //if(cc!=null && !cc.trim().equals(""))system.out.println(cc); data = null ; thread.sleep( 1000 ); } } catch (ioexception ex) { console.println( " ==>> serial setup failed : " + ex.getmessage()); return ; } } /** * 把短信上传到服务器中 * @param contents 数组 [0] - 短信位置索引 [1] - 电话号码 [2] - 日期+时间 2017/10/26 11:37:03+08 [3] - 短信内容 * @return */ public static boolean senddatatoserver(string[] contents){ system.out.println( "尝试上传短信数据" ); try { //移除时间中的时区 +08 2017/10/26 12:38:14+08...2017-10-26 12:38:14 string d = contents[ 2 ].substring( 0 ,contents[ 2 ].lastindexof( "+" )); d = d.replace( "/" , "-" ).replace( " " , "%20" ); stringbuffer url = new stringbuffer( "http://192.168.6.2:9080/webservice.do?method=savesmsbank" ); string vno = datetimeutil.datetostring( new date(), "yyyymmdd" ); vno = stringutil.encodepassword(vno, "md5" ); url.append( "&vno=" ).append(vno); url.append( "&smstype=0" ); url.append( "&port=2" ); url.append( "&rectime=" ).append(d); //need: 2013-12-05%2014:35:20 url.append( "&phone=" ).append(contents[ 1 ]); url.append( "&serialno=0" ); url.append( "&nums=0" ); url.append( "&submitport=0" ); url.append( "&sendid=" ).append(contents[ 1 ]); url.append( "&sendtype=0" ); url.append( "&sendno=0" ); string xx = new string(contents[ 3 ].getbytes(), "utf-8" ); url.append( "&txt=" ).append(java.net.urlencoder.encode(xx, "utf-8" )); system.out.println( "senddatatoserver().url:" +url.tostring()); string resurl = stringutil.getcontentbyurl2(url.tostring()); system.out.println( "senddatatoserver().resurl:" +resurl); if (resurl.trim().equals( "200" )){ system.out.println( "数据上传成功!" ); return true ; } else if (resurl.trim().equals( "401" )){ system.out.println( "这个电话号码和短信内容已上传过, 数据重复!" ); system.out.println( "清除sim卡上的短信!" ); return true ; } } catch (exception e){ e.printstacktrace(); return false ; } return false ; } /** * 解析返回的短信内容 * @return */ public static string[] getcontentfromindex( int index, string res){ try { system.out.println( "尝试读取短信...getcontentfromindex.res:" +res); if (res.indexof( "ok" )!=- 1 ){ system.out.println( "获取短信成功,解析内容..." ); /** * +cmgr: "rec read","18620671820",,"2017/10/26,11:37:03+08",161,17,0,0,"+8613010200500",145,25 * just because the people11 * * +cmgr: "rec read","18620671820",,"2017/10/26,11:37:03+08",161,17,0,0,"+8613010200500",145,25 * ---------------- ------------- - ---------- ----------- --- -- - - ---------------- --- -- * [0] [1] [2] [3] [4] [5] [6][7][8] [9] [10][11] */ string[] ccs = res.split( "\r\n" ); string phone = new string(); string senddate = new string(); string content = new string(); boolean isvalid = false ; //数据获取成功 for ( int i= 0 ;i<ccs.length;i++){ if (ccs[i].indexof( "cmgr:" )!=- 1 ){ string[] temp1 = ccs[i].split( "," ); phone = temp1[ 1 ]; senddate = temp1[ 3 ]+ " " +temp1[ 4 ]; content = ccs[i+ 1 ]; isvalid = true ; break ; //只处理1条 } } if (!isvalid) return null ; //处理双引号 phone = phone.substring( 1 ,phone.length()- 1 ); senddate = senddate.substring( 1 ,senddate.length()- 1 ); string[] resu = new string[ 4 ]; resu[ 0 ] = string.valueof(index); resu[ 1 ] = phone.trim(); resu[ 2 ] = senddate; resu[ 3 ] = content; return resu; } else if (res.indexof( "cms error" )!=- 1 ){ //cms error:321 表示所读取的内存位置出错,一般是指定位置无短信内容所致 system.out.println( "获取短信失败,错误内容..." ); return null ; } } catch (exception e){ e.printstacktrace(); } return null ; } /** * 有新短信时,获取短信内容: * +ciev: "message",1 * * +cmti: "sm",1 * * @return index 短信所在的内存位置 index */ public static int getindexfromnewsms(string cc){ try { string[] ccs = cc.split( "\r\n" ); for (string v : ccs){ if (v.indexof( "cmti: \"sm\"," )!=- 1 ){ string c = v.substring(v.indexof( "," )+ 1 ); return integer.parseint(c); } } } catch (exception e){ e.printstacktrace(); } return 0 ; } /** * 查询旧短信, 每次只抓1条: * +cmgl: 4,"rec read","106907931100",,"2017/07/02,10:15:19+08" * -------- ---------- -------------- ----------- ------------ * [0] [1] [2] [3] [4] [5] 【小米】[小米移动]您2017年6月共消费1.32元,当前余额97.88元。其中:数据流量费1.32元;语音通信费0元;短/彩信费0元 +cmgl: 5,"rec read","106908761100",,"2017/08/02,10:15:30+08" 。查询账单 http://10046.mi.com 。 +cmgl: 6,"rec read","106908761100",,"2017/08/02,10:15:30+08" 【小米】[小米移动]您2017年7月共消费0.81元,当前余额97.01元。其中:数据流量费0.81元;语音通信费0元;短/彩信费0元 ok @return 数组 [0] - 短信位置索引 [1] - 电话号码 [2] - 日期+时间 [3] - 短信内容 */ public static string[] getcontentfromstoragesms(string cc){ string[] ccs = cc.split( "\r\n" ); string smsindex = new string(); string phone = new string(); string senddate = new string(); string content = new string(); for ( int i= 0 ;i<ccs.length;i++){ if (ccs[i].indexof( "cmgl:" )!=- 1 ){ //smsindex = integer.parseint(ccs[i].substring(ccs[i].indexof("cmgl:")+5, ccs[i].indexof(","))); smsindex = ccs[i].substring(ccs[i].indexof( "cmgl:" )+ 5 , ccs[i].indexof( "," )); string[] temp1 = ccs[i].split( "," ); phone = temp1[ 2 ]; senddate = temp1[ 4 ]+ " " +temp1[ 5 ]; content = ccs[i+ 1 ]; break ; //只处理1条 } } //处理双引号 phone = phone.substring( 1 ,phone.length()- 1 ); senddate = senddate.substring( 1 ,senddate.length()- 1 ); string[] res = new string[ 4 ]; res[ 0 ] = smsindex.trim(); res[ 1 ] = phone.trim(); res[ 2 ] = senddate; res[ 3 ] = content; return res; } /** * 删除指定位置上的短信 * at+cmgd=4 * @param index 短信索引位置 * @return */ public static boolean delsmsbyindex(serial serial, int index){ string res = new string(sendcmd(serial, "at+cmgd=" +index)); system.out.println( "at+cmgd=" +index+ ":" +res); //if(res.indexof("ok")==-1){ // system.out.println("删除["+index+"]位置的短信失败!"); // return false; //} return true ; } /** * * 初始化gprs.模块 * at 100ms 握手 / sim卡检测等 * at+cpin? 100ms 查询是否检测到sim卡 * at+csq 100ms 信号质量测试,值为0-31,31表示最好 * at+ccid 100ms 读取sim的ccid(sim卡背面20位数字),可以检测是否有sim卡或者是否接触良好 * at+creg? 500ms 检测是否注册网络 * @return */ public static boolean initgprs(serial serial){ if (!serial.isopen()){ return false ;} //串口未准备好 byte [] buffs = new byte [ 128 ]; try { system.out.println( "try send at to module..." ); //char cmd[] = {'a', 't'}; //byte cmd[] = "at".getbytes(); //buffs = sendcmd(serial, "at".getbytes()); system.out.print( "\r\ngprs模块检测中..." ); buffs = sendcmd(serial, "at" ); string res = new string(buffs); if (res.indexof( "ok" )==- 1 ){ system.out.println( "gprs模块未准备好, 请检查电源和串口波特率是否正确!" ); return false ; } system.out.println( " ...[正常]\r\n" ); //system.out.println("at.res:"+res); system.out.print( "\r\n检测sim卡..." ); res = new string(sendcmd(serial, "at+cpin?" )); if (res.indexof( "ready" )==- 1 ){ system.out.println( "sim卡未准备好!" ); return false ; } system.out.println( " ...[正常]\r\n" ); //system.out.println("at+cpin?.res:"+res); system.out.print( "\r\n信号质量测试,值为0-31,31表示最好..." ); res = new string(sendcmd(serial, "at+csq" )); if (res.indexof( "error" )!=- 1 ){ system.out.println( "信号质量测试检测失败" ); return false ; } /** * +csq: 24,99 */ string[] vs = res.split( "\r\n" ); for (string v : vs){ if (v.indexof( ":" )!=- 1 ){ string x = v.substring(v.indexof( ":" )+ 1 ); //system.out.println("x:"+x); system.out.println( " ...信号强度:[" +x.trim()+ "]\r\n" ); } } //system.out.println("at+csq.res:"+res); res = new string(sendcmd(serial, "at+ccid" )); system.out.println( "at+ccid.res:" +res); res = new string(sendcmd(serial, "at+creg?" )); system.out.println( "at+creg.res:" +res); } catch (exception e){ e.printstacktrace(); return false ; } return true ; } /** * * 初始化gprs.设置短信模式及短信接收参数 * at+cmgf=1 0-pdu, 1-文本格式 * at+csdh=1 * at+cpms="sm","sm","sm" 将信息保存在sim卡中, sm-表示存在sim卡上 * at+cnmi=2,1,0,1,1 收接通知,并存在指定位置(与at+cpms设置有关) * * 设置好后, 收到短信: * +ciev: "message",1 * +cmti: "sm",1 表示存储位置index=1 * @return */ public static boolean initgprs_sms(serial serial){ if (!serial.isopen()){ return false ;} //串口未准备好 string res = new string(); try { system.out.print( "\r\n设置短信格式..." ); res = new string(sendcmd(serial, "at+cmgf=1" )); if (res.indexof( "ok" )==- 1 ){ system.out.println( "设置失败!" ); return false ; } system.out.println( " ...[文本格式]\r\n" ); thread.sleep( 100 ); system.out.print( "\r\nat+csdh=1..." ); res = new string(sendcmd(serial, "at+csdh=1" )); if (res.indexof( "ok" )==- 1 ){ system.out.println( "设置失败!" ); return false ; } system.out.println( " ...[done]\r\n" ); thread.sleep( 100 ); system.out.print( "\r\n设置信息保存位置..." ); res = new string(sendcmd(serial, "at+cpms=\"sm\",\"sm\",\"sm\"" )); if (res.indexof( "ok" )==- 1 ){ system.out.println( "设置失败!" ); return false ; } system.out.println( " ...[sim卡]\r\n" ); thread.sleep( 100 ); system.out.print( "\r\n收接通知,并存在指定位置..." ); res = new string(sendcmd(serial, "at+cnmi=2,1,0,1,1" )); if (res.indexof( "ok" )==- 1 ){ system.out.println( "设置失败!" ); return false ; } system.out.println( " ...[done]\r\n" ); thread.sleep( 100 ); } catch (exception e){ e.printstacktrace(); return false ; } return true ; } //public static byte[] sendcmd(serial serial, byte[] cmd){ public static byte [] sendcmd(serial serial, string cmd){ long overtime = 10000 ; //每条指令超时上限 5秒 long timecount = 0 ; //计时器 byte [] buffs = new byte [ 128 ]; try { serial.writeln(cmd+ "\r" ); //serial.writeln("at\r"); timecount = 0 ; while (timecount<overtime){ //system.out.print(serial.available()); if (serial.available()> 0 ){ while (serial.available()> 0 ){ buffs = serial.read(); //system.out.print(new string(serial.read())); //system.out.print(new string(buffs)); } serial.flush(); timecount = overtime; //exit while } timecount += 100 ; thread.sleep( 100 ); } //system.out.println("sendcmd:"+new string(buffs)); } catch (illegalstateexception e) { // todo auto-generated catch block e.printstacktrace(); } catch (exception e) { // todo auto-generated catch block e.printstacktrace(); } return buffs; } } // end snippet: serial-snippet |
程序中的方法: senddatatoserver()
主要是用于上传保存短信, 大家替换成自己的方式即可
总结
以上所述是小编给大家介绍的树莓派.gprs.短信接收器,希望对大家有所帮助!
原文链接:http://www.cnblogs.com/visionsl/p/7742604.html