项目笔记(4):stm32+cat1模块连接阿里云物联网平台的代码实现
一、代码框架

1、新建一个4G文件,在把如图所示的4个.c文件放到工程里,加入.h文件路径
2、
2.1 cstring.c包含一些字符串操作函数
2.2 fat_core.c用于实现数据发送给模块
2.3 AXK_MD5.c 用于实现阿里的登录信息的hamcMD5算法
2.4 connet_aliyun.c就是连接阿里云的流程代码实现,包括了一机一密和一型一密。通过全局变量来切换注册方式regesit_mode
3、主函数说明,精简代码如下
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
/*继电器控制管教和指示灯管脚初始化*/
IO_Init();
Power_ON_init(); // 开机检测脚和电源使能脚初始化
init_all(); // 变量初始?
uart_init(); //串口初始化为115200
Timer2_Init(99,7199); //定时10ms,作为南华机型粘度检测的计时基准。
reg_fat_uart_send_byte(uart2_send_byte);
read_stmflash();
SysTick_init(10); //滴答定时器10ms中断初始化
while(1)
{
if(HttpConnectFlag == 0)
{
connet_aliyun(regesit_mode);
}
else
{
}
}
}
3.1 定义了定时器2,中断10ms;
3.2 reg_fat_uart_send_byte(uart2_send_byte);//注册用于和模块的通信串口,这里是串口2和模块通信,串口1用于打印日志
3.3 在while主循环中调用连接阿里云函数connet_aliyun(regesit_mode),如果连接上了,就在else语句下添加自己的要实现的发布消息和处理服务器下发的消息
4、连接阿里云流程的代码解析
void connet_aliyun(u8 mode)
{
static int state = MD_RESET;
int ret = 0;
switch (state)
{
//复位模块
case MD_RESET:
if (module_reset())
{
wait_timeout(LTE_POWER_ON_WAIT_TIME);
state = MD_AT_REQ;
}
break;
//AT握手
case MD_AT_REQ:
if (fat_send_wait_cmdres_nonblocking("AT\r\n", 3000))//3s
{
if (fat_cmdres_keyword_matching("OK"))
{
ucErrorTimes = 0;
state = MD_WORK_STA_CHK;
}
else
{
if (ucErrorTimes++ > 30)
{
state = MD_ERR;
}
}
}
break;
//模块状态检测
case MD_WORK_STA_CHK:
ret = module_is_ready();
if (ret == MD_OK)
{
ucErrorTimes = 0;
state = MD_CONNETINIT;
}
else if (ret == MD_ERR)
{
state = MD_FLIGHTMODE;
}
break;
//连接参数初始化
case MD_CONNETINIT:
ret = module_connet_parm_init();
if (ret == MD_OK)
{
state = MD_CONNETED;
}
else if (ret == MD_ERR)
{
state = MD_FLIGHTMODE;
}
break;
//数据通信处理
case MD_CONNETED:
ret = MqttConnect();
if( ret == MD_OK)
{
HttpConnectFlag = 1;
printf("阿里云连接成功\r\n");
}
else if (ret == MD_ERR)
{
HttpConnectFlag = 0;
state = MD_FLIGHTMODE;
}
break;
//飞行模式处理
case MD_FLIGHTMODE:
ret = module_flightmode();
if(ret == MD_WORK_STA_CHK)
{
state = MD_WORK_STA_CHK;
}
else if(ret == MD_ERR)
{
ucFlightModeTimes = 0;
state = MD_ERR;
}
break;
//错误
case MD_ERR:
ucErrorTimes = 0;
state = MD_RESET;
break;
default:
break;
}
}
4.1先复位模块(stm32连接模块的电源使能脚),拉低再拉高,然后等待2S(非阻塞等待)
4.2然后发送AT指令等待应答,调用的函数fat_send_wait_cmdres_nonblocking(或者fat_send_wait_cmdres_blocking),函数原型
* @description: 发送命令后,开启定时计数,在设置的定时时间到达后返回1,用户此时可以进行对命令的响应结果进行处理
* @param cmd:要发送的命令
* @param timeoutval:定时时间
* @return 0:未到达定时时间,1:定时时间到达
*/
int fat_send_wait_cmdres_blocking(char *cmd, unsigned short int timeoutval)
{
/* 发送命令,开启定时计数 */
if (ucTimeOutStartFlg == 0)
{
FAT_DBGPRT_INFO("uart send: %s\r\n",cmd);
fat_uart_clean();
fat_uart_send_str((unsigned char *)cmd);
usTimeOutVal = timeoutval / FAT_TIMER_VAL;
ucTimeOutStartFlg = 1;
}
/* 到达设定的定时时间 */
if (ucTimeOutValReachedFlg)
{
ucTimeOutValReachedFlg = 0;
ucTimeOutStartFlg = 0;
usTimeOutCount = 0;
return 1;
}
return 0;
}
/**
* @description: 发送命令后,开启定时计数,在设置的定时时间到达后或命令有响应数据时返回1,用户此时可以进行对命令的响应结果进行处理
* @param cmd: 要发送的命令
* @param timeoutval: 定时时间
* @return 0:未达到定时时间,1:定时时间到达或命令有响应数据
*/
int fat_send_wait_cmdres_nonblocking(char *cmd, unsigned short int timeoutval)
{
/* 发送命令,开启定时计数 */
if (ucTimeOutStartFlg == 0)
{
FAT_DBGPRT_INFO("uart send: %s\r\n",cmd);
fat_uart_clean();
fat_uart_send_str((unsigned char *)cmd);
usTimeOutVal = timeoutval / FAT_TIMER_VAL;
ucTimeOutStartFlg = 1;
}
/* 到达设定的定时时间或接收到一帧数据 */
if (ucTimeOutValReachedFlg || ucFatUartRecvFinishFlg)
{
ucTimeOutValReachedFlg = 0;
ucTimeOutStartFlg = 0;
usTimeOutCount = 0;
return 1;
}
return 0;
}
2者的区别是函数说明已经写了(函数都是非阻塞的),很多别人的模块送的例程都是死循环等待,这种在实际工程中步推荐
然后调用fat_cmdres_keyword_matching("OK")去查询模块是否返回"OK"字符串,函数原型
/**
* @description: 命令响应数据中查找关键字
* @param <keyword> 要被检索的关键字
* @return 0:未检索到关键字, 1:检索到了关键字
*/
int fat_cmdres_keyword_matching(char *keyword)
{
/* 命令有响应,在响应数据中查找关键字 */
if (ucFatUartRecvFinishFlg)
{
FAT_DBGPRT_INFO("uart recv: %s\r\n",USART2_RX_BUF);
/* 检索检索到关键词 */
if (strstr((const char *)(USART2_RX_BUF), keyword) != NULL)
{
return 1;
}
ucFatUartRecvFinishFlg = 0;
}
return 0;
}
4.3 模块检测到后就是对模块进行初始化配置
int module_is_ready(void)
{
u8 *p;
switch (ucStateNum)
{
//关闭AT命令回显
case 0x00:
if (fat_send_wait_cmdres_blocking("ATE0\r\n", 1000))
{
//收到OK
if (fat_cmdres_keyword_matching("OK"))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
//读卡
case 0x01:
if (fat_send_wait_cmdres_blocking("AT+CPIN?\r\n", 1000))
{
//收到+CPIN: READY
if (fat_cmdres_keyword_matching("+CPIN: READY"))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
//查询信号质量
case 0x02:
if (fat_send_wait_cmdres_blocking("AT+CSQ\r\n", 1000))
{
//收到OK
if (fat_cmdres_keyword_matching("OK"))
{
//收到的是99(射频信号未初始化)
if (fat_cmdres_keyword_matching("+CSQ: 99,99"))
{
//发送30次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
else
{
//信号值在SIGNALMIN~SIGNALMAX这个区间
if (match_csq((char*)USART2_RX_BUF, SIGNALMIN, SIGNALMAX))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
ucStateNum = MD_ERR;
}
}
}
//没收到应答
else
{
//发送30次不应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
//查看当前GPRS附着状态
case 0x03:
if (fat_send_wait_cmdres_blocking("AT+CGATT?\r\n", 1000))
{
//收到+CGATT: 1
if (fat_cmdres_keyword_matching("+CGATT: 1"))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送30次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
//查看IMEI
case 0x04:
if (fat_send_wait_cmdres_blocking("AT+CGSN\r\n", 1000))
{
if (fat_cmdres_keyword_matching("OK"))
{
p = StrBetwString((char *)(USART2_RX_BUF),"\r\n","\r\n");
printf((char *)p);
ucErrorTimes = 0;
ucStateNum = MD_OK;
}
else
{
//发送30次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
//错误跳至飞行模式
case MD_ERR:
ucStateNum = 0;
return MD_ERR;
//完成
case MD_OK:
ucStateNum = 0;
return MD_OK;
default:
break;
}
return 0;
}
关闭回显,检测卡是否插入,网络是否准备好,读取卡的IMEI(后面的设备名字devicename都用到),都检测成功返回OK,否则返回错误
4.4 连接阿里云
int module_connet_parm_init(void)
{
u8 p[200];
u16 DOWNLOAD_LEN = 0;
static u8 md5flag = 0;
switch (ucStateNum)
{
//关闭移动场景
case 0x00:
if (fat_send_wait_cmdres_blocking("AT+CIPSHUT\r\n", 1000))
{
//收到SHUT OK
if (fat_cmdres_keyword_matching("SHUT OK"))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送5次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
//关闭移动场景后等待2秒
case 0x01:
if (wait_timeout(2000))
{
ucStateNum++;
}
break;
//设置承载类型为GPRS
case 0x02:
if (fat_send_wait_cmdres_blocking("AT+SAPBR=3,1,\"Contype\",\"GPRS\"\r\n", 1000))
{
//收到OK
if (fat_cmdres_keyword_matching("OK"))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
//配置承载之APN
case 0x03:
if (fat_send_wait_cmdres_blocking("AT+SAPBR=3,1,\"APN\",\"CMIOT\"\r\n", 1000))
{
//收到OK
if (fat_cmdres_keyword_matching("OK"))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
//打开承载
case 0x04:
if (fat_send_wait_cmdres_blocking("AT+SAPBR=1,1\r\n", 2000))
{
//收到OK
if (fat_cmdres_keyword_matching("OK"))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
//初始化 HTTP 服务
case 0x05:
if(regesit_mode == 0)
{
ucErrorTimes = 0;
ucStateNum = 0X0D;
}
else if(regesit_mode == 1)
{
if( md5flag == 0)
{
md5flag = 1;
STMFLASH_Read(DeviceSecret_SAVE_ADDR,(u16*)DeviceSecret,20); //从flash中读出DeviceSecret
printf("xcm read DeviceSecret=%s\r\n",DeviceSecret);
if(STMFLASH_ReadHalfWord(DeviceSecret_SAVE_OK)==0x88)//DeviceSecret_SAVE_OK==0x88表示设备已经获取过设备密钥,无需再次获取
{
ucErrorTimes = 0;
md5flag =0;
ucStateNum = 0X0D;
}
}
else
{
// cat1_send_cmd("AT+HTTPINIT","OK",20);
if (fat_send_wait_cmdres_nonblocking("AT+HTTPINIT\r\n", 2000))
{
//收到OK
if (fat_cmdres_keyword_matching("OK"))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 10)
{
md5flag = 0;
ucStateNum = MD_ERR;
}
}
}
}
}
break;
case 0x06:
md5flag = 0;
if (fat_send_wait_cmdres_nonblocking("AT+HTTPPARA=\"URL\",\"https://iot-auth.cn-shanghai.aliyuncs.com/auth/register/device\"\r\n", 2000))
{
//收到OK
if (fat_cmdres_keyword_matching("OK"))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
case 0x07:
if (fat_send_wait_cmdres_nonblocking("AT+HTTPPARA=\"USER_DEFINED\",\"Content-Type: application/x-www-form-urlencoded\"\r\n", 2000))
{
//收到OK
if (fat_cmdres_keyword_matching("OK"))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
// 告诉HTTP服务器接下来要发送的数据长度
case 0x08:
if(md5flag == 0)
{
get_HmacMD5(1);
md5flag = 1;
}
sprintf((char*)p,"productKey=%s&deviceName=%s&random=%s&sign=%s&signMethod=HmacMD5",\
(u8*)ProductKey,(u8*)DeviceName,(u8*)random,(u8*)keyOutput);
DOWNLOAD_LEN=strlen((char*)p);
sprintf((char*)p,"AT+HTTPDATA=%d,20000\r\n",DOWNLOAD_LEN);
if (fat_send_wait_cmdres_nonblocking((char*)p, 2000))
{
//收到OK
if (fat_cmdres_keyword_matching("DOWNLOAD"))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 5)
{
ucStateNum = MD_ERR;
}
}
}
break;
// 向HTTP服务发送数据
case 0x09:
md5flag = 0;
sprintf((char*)p,"productKey=%s&deviceName=%s&random=%s&sign=%s&signMethod=HmacMD5\r\n",\
(u8*)ProductKey,(u8*)DeviceName,(u8*)random,(u8*)keyOutput);
if (fat_send_wait_cmdres_nonblocking((char*)p, 2000))
{
//收到OK
if (fat_cmdres_keyword_matching("OK"))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
case 0x0a:
if (fat_send_wait_cmdres_nonblocking("AT+HTTPACTION=1\r\n", 2000))
{
//收到OK
if (fat_cmdres_keyword_matching("+HTTPACTION: 1,200"))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 5)
{
ucStateNum = MD_ERR;
}
}
}
break;
//获取到注册数据
case 0x0b:
if (fat_send_wait_cmdres_nonblocking("AT+HTTPREAD\r\n", 2000))
{
//收到OK
if (fat_cmdres_keyword_matching("+HTTPREAD:"))
{
get_Registration_info();
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 5)
{
ucStateNum = MD_ERR;
}
}
}
break;
// 关闭http
case 0x0c:
if (fat_send_wait_cmdres_nonblocking("AT+HTTPTERM\r\n", 2000))
{
//收到OK
if (fat_cmdres_keyword_matching("OK"))
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 5)
{
ucStateNum = MD_ERR;
}
}
}
break;
//MQTT链接阿里云
case 0X0D:
if(md5flag == 0)
{
get_HmacMD5(0);
md5flag = 1;
}
sprintf((char*)p,"AT+MCONFIG=\"%s|securemode=3,signmethod=hmacmd5|\",\"%s&%s\",\"%s\"\r\n",\
(u8*)DeviceName,(u8*)DeviceName,(u8*)ProductKey,keyOutput);
if (fat_send_wait_cmdres_nonblocking((char*)p, 2000))
{
//收到OK
if (fat_cmdres_keyword_matching("OK"))
{
md5flag = 0;
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
// SSL链接
case 0X0E:
sprintf((char*)p,"AT+SSLMIPSTART=\"%s.iot-as-mqtt.cn-shanghai.aliyuncs.com\",1883\r\n",(u8*)ProductKey);
if (fat_send_wait_cmdres_blocking((char*)p, 2000))
{
//收到OK
if( fat_cmdres_keyword_matching("CONNECT OK") || (fat_cmdres_keyword_matching("ALREADY")) )
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 3)
{
ucStateNum = MD_ERR;
}
}
}
break;
case 0X0F:
if (fat_send_wait_cmdres_blocking("AT+MCONNECT=1,130\r\n", 2000))
{
//收到OK
if( fat_cmdres_keyword_matching("CONNACK OK") )
{
ucErrorTimes = 0;
ucStateNum = MD_OK;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 3)
{
ucStateNum = MD_ERR;
}
}
}
break;
//完成
case MD_OK:
ucStateNum = 0;
return MD_OK;
//错误跳至飞行模式
case MD_ERR:
ucStateNum = 0;
return MD_ERR;
default:
break;
}
return 0;
}
步骤00-04都是常规流程,按照上篇文章的笔记3初始化网络,然后05步判断是一机还是一型一密注册
4.4.1 一机一密
跳转到0x0d这步,调用get_HmacMD5算出用户密码,然后按照步骤执行,成功返回MD_OK
4.4.2一型一密
先判断之前是否有一型一密注册成功过,注册过则跳转到一机一密的步骤
否则,就HTTP方式获取DeviceSecret然后存在flash指定位置并置位标志,然后跳转到一机一密步骤继续执行
5、注册成功后,则调用注册成功回调函数MqttConnect
int MqttConnect()
{
u8 p[500];
u8 at_buf[4];
switch(ucStateNum)
{
case 0:
sprintf((char*)p,"AT+MSUB=\"/%s/%s/user/get\",0\r\n",(u8*)ProductKey,(u8*)DeviceName);
if(fat_send_wait_cmdres_blocking((char*)p, 1000))
{
if( fat_cmdres_keyword_matching("SUBACK") )
{
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
case 1:
ucErrorTimes = 0;
ucStateNum++;
break;
// if(fat_send_wait_cmdres_nonblocking("AT+CIPGSMLOC=1,1\r\n",3000))//获取基站定位
// {
// if (fat_cmdres_keyword_matching("+CIPGSMLOC:"))
// {
// get_locatian();
//
// }
// }
case 2:
if(fat_send_wait_cmdres_blocking("AT+CIPGSMLOC=1,1\r\n",3000))//获取基站定位
{
if (fat_cmdres_keyword_matching("+CIPGSMLOC: 0"))
{
get_locatian();
ucErrorTimes = 0;
ucStateNum++;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
case 3:
/* 读取机器剩余清洗次数,并发送到串口屏 */
// AT24CXX_Read(ALLOW_CLEAN_COUNT, (u8*)at_buf, 2);
// used_count = (at_buf[0]<<8) + at_buf[1];
//
// AT24CXX_Read(ALL_CLEAN_COUNT,(u8*)at_buf,2);
// total_count = (at_buf[0]<<8) + at_buf[1];
used_count = 200;
total_count = 0;
sprintf((char*)p,"AT+MPUB=\"/sys/%s/%s/thing/event/property/post\",0,0,\"{\\22id\\22:\\22123\\22,\\22version\\22:\\221.0\\22,\
\\22params\\22:{\\22GeoLocation\\22:{\\22Longitude\\22:%s,\\22Latitude\\22:%s},\\22allcleancount\\22:%d,\\22cleanreleas\\22:%d,\\22RunningState\\22:%d},\
\\22method\\22:\\22thing.event.property.post\\22}\"\r\n",(u8*)ProductKey,(u8*)DeviceName,Longitude,Latitude,total_count,used_count,0);//部分字符4G模块无法识别,需转义 “转义后为\\22
if(fat_send_wait_cmdres_nonblocking((char *)p, 1000))
{
if (fat_cmdres_keyword_matching("OK"))
{
ucErrorTimes = 0;
ucStateNum = MD_OK;
}
else
{
//发送10次得不到正确应答
if (ucErrorTimes++ > 10)
{
ucStateNum = MD_ERR;
}
}
}
break;
//错误跳至飞行模式
case MD_ERR:
ucStateNum = 0;
return MD_ERR;
//完成
case MD_OK:
ucStateNum = 0;
return MD_OK;
default:
break;
}
return 0;
}
订阅主题和上传设备初始信息
更多推荐
所有评论(0)