树莓派.GPRS.短信接收器

  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("

  ");

  String phone = new String();

  String sendDate = new String();

  String content = new String();

  boolean isvalid = false; //数据获取成功

  for(int i=0;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("

  ");

  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("

  ");

  String smsIndex = new String();

  String phone = new String();

  String sendDate = new String();

  String content = new String();

  for(int i=0;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("

  GPRS模块检测中...");

  buffs = sendCMD(serial, "AT");

  String res = new String(buffs);

  if(res.indexOf("OK")==-1){

  System.out.println("GPRS模块未准备好, 请检查电源和串口波特率是否正确!");

  return false;

  }

  System.out.println(" ...[正常]

  ");

  //System.out.println("AT.res:"+res);

  System.out.print("

  检测SIM卡...");

  res = new String(sendCMD(serial, "AT+CPIN?"));

  if(res.indexOf("READY")==-1){

  System.out.println("SIM卡未准备好!");

  return false;

  }

  System.out.println(" ...[正常]

  ");

  //System.out.println("AT+CPIN?.res:"+res);

  System.out.print("

  信号质量测试,值为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("

  ");

  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()+"]

  ");

  }

  }

  //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("

  设置短信格式...");

  res = new String(sendCMD(serial, "AT+CMGF=1"));

  if(res.indexOf("OK")==-1){

  System.out.println("设置失败!");

  return false;

  }

  System.out.println(" ...[文本格式]

  ");

  Thread.sleep(100);

  System.out.print("

  AT+CSDH=1...");

  res = new String(sendCMD(serial, "AT+CSDH=1"));

  if(res.indexOf("OK")==-1){

  System.out.println("设置失败!");

  return false;

  }

  System.out.println(" ...[DONE]

  ");

  Thread.sleep(100);

  System.out.print("

  设置信息保存位置...");

  res = new String(sendCMD(serial, "AT+CPMS="SM","SM","SM""));

  if(res.indexOf("OK")==-1){

  System.out.println("设置失败!");

  return false;

  }

  System.out.println(" ...[SIM卡]

  ");

  Thread.sleep(100);

  System.out.print("

  收接通知,并存在指定位置...");

  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]

  ");

  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+"

  ");

  //serial.writeln("AT

  ");

  timecount = 0;

  while(timecount

  //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