作者:良知犹存
转载授权以及围观:欢迎添加微信公众号:羽林君
总述
现在物联网的概念越来越突出,软件云端小米IOT平台、阿里物联网云等等,嵌入式百度手环开源很久了,此外网上关于ESP8266接入网关进行远程控制的源码不计其数。
物联网嵌入式端开发使用中都是各大主流网络模块,其中无线方式通信方式区分的话,主要分为两类:一类是Zigbee、WiFi、蓝牙、Z-wave等短距离通信技术;另一类是LPWAN(low-powerWide-AreaNetwork,低功耗广域网),即广域网通信技术。
至于几种通信模块使用的技术区别,改天可以写一篇文章进行详细介绍。不过 今天我来介绍一下,我使用过的一款2G网络模块,在车载行驶中使用,在很恶劣的情况下,依旧可以保证网络快速链接。因为现在的网络模块都是差不多的,尤其是IOT、LORA以及2G 3G 4G这些通过基站进行组网的模块。
有兴趣的朋友可以看看百度手环的开源资料,后台回复 find me 加我好友进行分享。
2G:第二代移动通信技术加入更多的多址技术,包括TDMA和CDMA,同时2G是数字通信,因此在抗干扰能力上大大增强。第二代移动通信可以说对接下来的3G和4G奠定了基础,比如分组域的引入,和对空中接口的兼容性改造,使得手机不再只有语音、短信这样单一的业务,还可以更有效率的连入互联网(电路域也可以提供internet业务,只是相对来说分组域更适合internet业务)。2G主要的制式也是两个,分别是来自欧洲ETSI组织的GSM(GPRS/EDGE)和来自美洲以高通公司为主力的TIA组织的CDMA IS95/CDMA2000 1x。
看到这里大家就有疑问了,上面巴拉巴拉一堆介绍中,2G网络看上去都是很落后的了,毕竟现在5G都出了。2G都没人用了,有什么好讲的。那我这里就给2G正个名。
现在网络通信技术发展的很快,手机等终端对最新通信技术的支持也是很及时,但是也是在手机这块。要知道手机现在的售价是很高的,在长时间的快速更新换代中,消费者也逐渐接受了这些技术指标以及消费价格。但是在一些工业领域,物联网产品中,只要很小的带宽,网络覆盖率广,流量费用足够低,甚至需要低功耗的模块。IOT LORA就是现在物联网选择比较多的模块,但是IOT依旧 也有自己的缺点。
“NB-IOT实际使用没有理论宣传那么好,NB-IOT,功耗低、传输距离远、系统容量大,这些都是耳熟能详的好处了。但是,功耗低:在大部分场景下,NB并不比2G功耗低多少;传输距离远:现在2G网络全覆盖,NB-IOT还在铺网阶段,体现不出来优势;系统容量大:当前物联网产品远远没有把2G网络占满,更体现不出来NB能容纳更多设备的优势了。价格贵:NB-IOT模块比2G模块贵3-4倍,算上运营商补贴还是要贵不少。”
而2G由于通信模块成本较低,不到4G模块的三分之一。市面上的共享单车定位和开锁、POS刷卡支付也采用2G网络连接。所以2G网络现阶段应用还是比较广的,所以我们之前也是采用了2G网络模块进行的开发。
2G模块比较有名就是上海移远公司的M26模块,出来时间比较长了,但是M26只有GPRS的功能。
所以我们选择了中移的M6313模块GPRS和GNSS二合一模块。相关的资料现在也是很多了,一般我们用到是AT命令使用手册,通信流程示例手册。通信手册我开发的时候还没有,现在中移出了,挺开心的,又解决了开发者的一些难题。
首先我们开发使用是也是云,嵌入式端没有使用现在比较火的MQTT或者CoAP这些协议,而是直接使用TCP进行端口链接到服务器。
因为M6313模块的功能和M26很像,所以这个TCP链接过程是可以沿用的。
下面是当初开发时候的写一个联网状态机,最后基本达到在CSQ在1224时候,联网时候可以最快达到45秒。
由于状态机的代码太长太多了,掘金有字数限制,所以这里无法全部展示给大家,大家可以移步关注我的微信公众号进行查看全部代码内容。

这个状态机可以实现联网,设置模块自动心跳、切换IP等功能。具体的解释也都在代码里面展示了。
static void TCP_Conncect(const char* ipaddr, const char* ipport)
{
char* p = NULL;
p = (char*) malloc(100);
if(NULL == p)
{
LOG("malloc p error!\r\n");
}
else
{
memset(p, 0, 100);
sprintf(p, "AT+QIOPEN=\"TCP\",\"%s\",%s",ipaddr, ipport);
mdm_send_cmd_noack((u8*)p);
LOG("%s\r\n", p); //DEBUG
}
free(p);
}
/*
******************************************************************
* @brief 进行IP修改时候IP正确性标志清零
* @author Conqueror(征服者)
* @version V1.0
* @date 2019-6-10
******************************************************************
*/
u8 IsIPvalid = 0;
static void ChangeIP(void)
{
IsIPvalid = 0;
dprintf("Clear IsIPvalid!\r\n");
}
/*
******************************************************************
* @brief timer.c进行调用此函数进行判断IP是否正常通讯
* @author Conqueror(征服者)
* @version V1.0
* @date 2019-6-17
******************************************************************
*/
TimTypeDef SecIPTimeManage;
void SecIPManageTimer(void)
{
if(SecIPTimeManage.stat == TON)
{
(SecIPTimeManage.cunt < SecIPTimeManage.des)?(SecIPTimeManage.cunt++):\
(TimerManageInit(&SecIPTimeManage,TOFF,0),IsIPvalid=0);
}
}
/*
******************************************************************
* @brief IP为正确可用的,可以进行写进FLASH
* @author Conqueror(征服者)
* @version V1.0
* @date 2019-6-17
******************************************************************
*/
void IPValid(void)
{
if(devParam.secondTcp.addr[0] !=0)
{
IsIPvalid=1;
TimerManageInit(&SecIPTimeManage,TOFF,0);
LOG("IP Valid !\r\n");
}
}
/*
******************************************************************
* @brief 用于第一次连接新的IP地址的时候进行发送测试
* @author Conqueror(征服者)
* @version V1.0
* @date 2019-6-17
******************************************************************
*/
static void TestTCP(void)
{
if(cntMdm == 1 && devParam.secondTcp.addr[0] !=0)
{
M6313SendTestData();/*确认IP情况*/
TimerManageInit(&SecIPTimeManage,TON,2000);/*20s判断*/
}
}
/*
******************************************************************
* @brief 判断IP是否正确,不正确直接清零
* @author Conqueror(征服者)
* @version V1.0
* @date 2019-6-17
******************************************************************
*/
void IsTCPIPVaild(void)
{
if(gCnt.mdmTotalErr > 3&&mdmInfo.signalIntensity > 10)
{
memset(&devParam.secondTcp,0x00,sizeof(devParam.secondTcp));/*只要新的进入则清除之前的数据*/
}
if(IsIPvalid == 0 && SecIPTimeManage.stat == TOFF && cntMdm==1\
&& econdTcp.addr[0] !=0)
{
LOG("IPInvalid !\r\n");
mdmFSMPWRRSTInit(); /*断电重启*/
memset(&devParam.secondTcp,0x00,sizeof(devParam.secondTcp));/*只要新的进入则清除之前的数据*/
}
}
void mdmFSMPWRRSTInit(void)
{
MDM_STA = MDM_EXE_PWRRST_ACT; /*设置mdmComFSM状态*/
TBoxInfoType.userInfo.bit.cntMdm = 0; /*网络默认断开*/
SysFlag.mdmFSMRunSta = 1; /*启动MDM_FSM,TCP错误处理程序将在一定时间内不执行*/
gtimer.mdmFSMRun = gtimer.timer; /*MDM_FSM处理,开始时刻*/
dprintf("-mdmFSM_PWRRST_Init\r\n");
}
void STOP_MDM_FSM(void)
{
SysFlag.mdmFSMRunSta = 0; /*MDM_FSM强制结束*/
gtimer.mdmFSMRun = 0;
}
void mdmComFSM(void)
{
static u8 iTimes = 0; /*发AT CMD次数*/
static u8 mdmPwrOnCnt = 0; /*MDM开机次数*/
if(MDM_STA != MDM_IDLE_STA) /*FSM运行中,刷新*/
{
refreshWorkTimer();
}
switch(MDM_STA)
{
case MDM_IDLE_STA:
break;
/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*模块断电复位*/
case MDM_EXE_PWRRST_ACT:
LOG("*%d<-:MDM_PWR_RST[T:%d]\r\n", MDM_EXE_PWROFF_ACT, gtimer.timer);
MDM_PWR_EN_L;
gtimer.mdmFSMTimerout = 0;
MDM_STA = MDM_WAIT_PWRRST_RES;
break;
case MDM_WAIT_PWRRST_RES:
if(gtimer.mdmFSMTimerout > 2000) /*超过10秒*/
{
gtimer.mdmFSMTimerout = 0;
MDM_PWR_EN_H;
LOG("MDM_PWR_EN_H!\r\n");
MDM_STA = MDM_EXE_PWRON_ACT;
LOG("MDM_PWR_RST Finish!\r\n");
}
break;
/*模块关机*/
case MDM_EXE_PWROFF_ACT: /*模块关机:开机状态模块的PWRKEY拉低1s关机*/
LOG("\r\n*Step:%d,MDM_EXE_PWROFF_ACT,time:%d\r\n", MDM_EXE_PWROFF_ACT, gtimer.timer);
MDM_PWRKEY_EN_H;
gtimer.mdmFSMTimerout = 0;
MDM_STA = MDM_WAIT_PWROFF_RES;
break;
case MDM_WAIT_PWROFF_RES:
if(gtimer.mdmFSMTimerout > 200) /*超过1秒*/
{
gtimer.mdmFSMTimerout = 0;
MDM_PWRKEY_EN_L; /*m6313的PWRKEY处产生高电平*/
MDM_STA = MDM_WAIT_PWROFF_DELAY;
LOG("MDM_PWRKEY_EN_L,PWROFF_time:%d\r\n", gtimer.timer);
}
break;
case MDM_WAIT_PWROFF_DELAY:
if(gtimer.mdmFSMTimerout > 2400) /*12秒等待,模块关机完成*/
{
LOG("MDM_POWER_OFF finish\r\n");
MDM_STA = MDM_EXE_PWRON_ACT;
}
break;
/*模块开机*/
case MDM_EXE_PWRON_ACT: /*模块开机:在关机状态模块的PWRKEY拉低2s开机*/
LOG("\r\n*Step:%d,MDM_EXE_PWRON_ACT,time:%d\r\n", MDM_EXE_PWRON_ACT,gtimer.timer);
M6313ComInit();
MDM_PWRKEY_EN_H;
gtimer.mdmFSMTimerout = 0;
MDM_STA = MDM_WAIT_PWRON_RES;
break;
case MDM_WAIT_PWRON_RES:
if(gtimer.mdmFSMTimerout > 400) /*超过2秒*/
{
MDM_PWRKEY_EN_L; /*PWRKEY开机流程结束*/
mdmPwrOnCnt++;
LOG("MDM_POWER_ON,,time:%d\r\n", gtimer.timer);
MDM_STA = MDM_TX_AT_CMD;
}
break;
/*检查MDM通信*/
case MDM_TX_AT_CMD:
LOG("\r\n*Step:%d,AT\r\n", MDM_TX_AT_CMD);//step 1
LOG("AT-%d-\r\n", iTimes);
mdm_send_cmd_noack((u8*)"AT");
iTimes++;
gtimer.mdmFSMTimerout = 0;
MDM_STA = MDM_WAIT_AT_RES;
break;
............................
............................
............................
default:
break;
}
}
这就是我分享的2G网络模块联网的过程哈,里面代码是实践过的,如果大家有什么更好的思路,欢迎分享交流哈。