现在已经是3G、4G的天下,但古老的GSM模式中有一个GPRS数据承载服务,是在2G网络下对上网需求的优化。
GSM是全球移动通讯系统Global System of Mobile communication,分属于2G通信。
GPRS是Gerneral Packer Radio Service的英文缩写,中文译为通用无线分组业务,具体来讲,GPRS是一项高速数据处理的科技,即以分组的“形式”把数据传送到用户手上。
GPRS是在GSM系统上发展出来的一种新的承载业务,目的是为GSM用户提供分组形式的数据业务。GPRS采用与GSM同样的无线调制标准、频带、突发结构、跳频规则以及同样的TDMA帧结构。
GPRS允许用户在端到端分组模式下发送和接收数据,而不需要利用电路交换模式的网络资源,从而提供了一种高效、低成本的无线分组数据业务。
部分GPRS模块内置了TCP/IP协议栈,可以很方便的通过厂家扩展的AT指令直接进行TCP或UDP通信。
这是在RTOS(RT-Thread Operating System)里面的一段控制Zigbee模块用GPRS通信方式和服务器通信的代码:
<!-- lang: cpp -->
void thread_gsm_entry(void *parameter)
{
uint16_t i = 0;
gsm_init_hw("uart3");
rt_kprintf("gsm init hardware success \n");
thread_gsm_at = rt_thread_create("gsm_at",
thread_gsm_at_entry, RT_NULL,
1024,
10, 10);
if (thread_gsm_at != RT_NULL)
{ rt_thread_startup(thread_gsm_at); }
gsm_power_switch();
rt_thread_delay(RT_TICK_PER_SECOND * 7);
for (i = 0; i < 5; i++)
{
rt_device_write(gsm_dev, 0, "AT\r\n", strlen("AT\r\n")); //测试连接是否正确
rt_thread_delay(RT_TICK_PER_SECOND * 1);
}
for (i = 0; i < 1; i++)
{
rt_device_write(gsm_dev, 0, "ATE1\r\n", strlen("ATE1\r\n")); //打开回显
rt_thread_delay(RT_TICK_PER_SECOND * 1);
}
for (i = 0; i < 5; i++)
{
rt_device_write(gsm_dev, 0, "AT+CSQ\r\n", strlen("AT+CSQ\r\n")); //检查信号
rt_thread_delay(RT_TICK_PER_SECOND * 1);
}
for (i = 0; i < 5; i++)
{
rt_device_write(gsm_dev, 0, "AT+CGREG?\r\n", strlen("AT+CGREG?\r\n"));//获取小区环境
rt_thread_delay(RT_TICK_PER_SECOND * 1);
}
for (i = 0; i < 1; i++)
{
rt_device_write(gsm_dev, 0, "AT+CGATT?\r\n", strlen("AT+CGATT?\r\n"));
rt_thread_delay(RT_TICK_PER_SECOND * 3); //激活
}
for (i = 0; i < 1; i++)
{
rt_device_write(gsm_dev, 0, "AT+CSTT\r\n", strlen("AT+CSTT\r\n"));
rt_thread_delay(RT_TICK_PER_SECOND * 5);
}
for (i = 0; i < 1; i++)
{
rt_device_write(gsm_dev, 0, "AT+CIICR\r\n", strlen("AT+CIICR\r\n"));
rt_thread_delay(RT_TICK_PER_SECOND * 5);
}
for (i = 0; i < 1; i++)
{
rt_device_write(gsm_dev, 0, "AT+CIFSR\r\n", strlen("AT+CIFSR\r\n"));
rt_thread_delay(RT_TICK_PER_SECOND * 1);
}
rt_device_write(gsm_dev,
0,
"AT+CIPSTART=\"TCP\",\"202.201.1.49\",12345\r\n",
strlen("AT+CIPSTART=\"TCP\",\"202.201.1.49\",12345\r\n"));
rt_thread_delay(RT_TICK_PER_SECOND * 5);
rt_device_write(gsm_dev, 0, "AT+CIPSEND=6\r\n", strlen("AT+CIPSEND=6\r\n"));
rt_thread_delay(RT_TICK_PER_SECOND * 3);
rt_device_write(gsm_dev, 0, "DEMO\r\n", strlen("DEMO\r\n"));
rt_thread_delay(RT_TICK_PER_SECOND * 5);
gsm_inited = RT_TRUE;
}
AT指令的步骤和作用如下所示:
服务器采用CentOS操作系统,安装JVM。因为GPRS来的数据是TCP报文格式,可以采用Socket编程进行接收,接收到的数据可以插入到数据库中进行保存。这样就可以保存和检索传感器节点的采样数据。
Server端代码采用Java编写,很容易。
import java.net.*;
import java.io.*;
public class Server
{
private ServerSocket ss;
private Socket socket;
private BufferedReader in;
private PrintWriter out;
public Server()
{
try
{
ss = new ServerSocket(12345);
socket = ss.accept();
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(),true);
while (true)
{
String line = in.readLine();
System.out.println("Monitoring data :" + line);
//out.close();
// in.close();
//socket.close();
}
ss.close();
}
catch (IOException e)
{}
}
public static void main(String[] args)
{
new Server();
}
}
需要注意的是千万不要使用out.println,可能会阻塞通信。
socket = ss.accept()要放在while循环外面持续接收数据。
也不要关闭socket和in输入流。Zigbee传来的数据是一个每隔3S的数据流。
RT-Threayd RTOS是一款来自中国的开源实时操作系统,由国内一些专业开发人员开发、维护。它不仅仅是一款高效、稳定的实时核心,也是一套面向嵌入式系统的软件平台,覆盖了全抢占的实时操作系统内核,小巧而与底层具体实现无关的文件系统,轻型的TCP/IP协议栈以及轻型的多窗口多线程图形用户界面。
这个例子里面有三个任务(进程):一个是DEMO进程,一个是RF进程,一个是GSM进程。我们看看任务的写法:
<!-- lang: cpp -->
//设置和DEMO进程相关的堆栈、数据结构和声明相关的函数
ALIGN(RT_ALIGN_SIZE) //DEMO线程,点灯线程
static rt_uint8_t demo_stack[ 2048 ];
static struct rt_thread thread_demo;
extern void thread_demo_entry(void *parameter);
ALIGN(RT_ALIGN_SIZE) //RF线程,接收Zigbee分节点的数据,通过GPRS与Server Socket通信
static rt_uint8_t rf_stack[ 2048 ];
static struct rt_thread thread_rf;
void thread_rf_entry(void *parameter);
ALIGN(RT_ALIGN_SIZE) //GSM线程,初始化GSM设置,初次通信,发送“DEMO”字符。
static rt_uint8_t gsm_stack[ 2048 ];
static struct rt_thread thread_gsm;
void thread_gsm_entry(void *parameter);
int rt_application_init(void)
{
rt_err_t result;
/* init demo thread */
result = rt_thread_init(&thread_demo,
"demo",
thread_demo_entry,
RT_NULL,
(rt_uint8_t*)&demo_stack[0],
sizeof(demo_stack),
30,
5);
if (result == RT_EOK)
{
rt_thread_startup(&thread_demo);
}
/* init rf thread */
result = rt_thread_init(&thread_rf,
"rf",
thread_rf_entry,
RT_NULL,
(rt_uint8_t*)&rf_stack[0],
sizeof(rf_stack),
10,
5);
if (result == RT_EOK)
{
rt_thread_startup(&thread_rf);
}
/* init gsm thread */
result = rt_thread_init(&thread_gsm,
"gsm",
thread_gsm_entry,
RT_NULL,
(rt_uint8_t*)&gsm_stack[0],
sizeof(gsm_stack),
20,
5);
if (result == RT_EOK)
{
rt_thread_startup(&thread_gsm);
}
return 0;
}
<!-- lang: cpp -->
if (gsm_inited)
{
rt_device_write(gsm_dev, 0, "AT+CIPSEND=", strlen("AT+CIPSEND="));
rt_device_write(gsm_dev, 0, num_temp, strlen(num_temp));
rt_device_write(gsm_dev, 0, "\r\n", strlen("\r\n"));
rt_thread_delay(RT_TICK_PER_SECOND * 1);
rt_device_write(gsm_dev, 0, data_temp, strlen(data_temp));
rt_thread_delay(RT_TICK_PER_SECOND * 3);
}
其中gsm_inited在GSM线程中被置位(gsm_inited = RT_TRUE),表明模块调试和初始化完毕,开始收发数据。
这是在RF线程(任务)中写的数据核心收发程序,data_temp中存储的是节点发来的数据, rt_device_write(gsm_dev, 0, data_temp, strlen(data_temp))使得数据传到服务器上。
[1].http://www.rt-thread.org/
[2].http://www.linuxidc.com/Linux/2012-09/69974.htm