本文附件
联网附件:
https://wwz.lanzoul.com/iCQ3P1y5yx7a
本工程全部文件:
https://ww0.lanzoul.com/imskP25ct2de
注意事项:
(1) 设备不在线, 是 容量不达标, 启动配置文件配置成 800即可
(2) 服务器在线, 但是上传属性undefine,
调试:核实变量标识符是否对应, 还有上传变量类型是否对应(调试方法,单个下发,逐个调试)
(3) 下发信息不成功,
调试:核实下发信息时,cjson拆包变量是否对应,是否赋值本地变量 (调试方法,单个下发,逐个调试)
(4)在了解完整个流程后, 可以通过修改服务器和key值,以及服务器变量,进行快速使用示例工程. 无需重复构建
引言目录
本文分为三部分
(1)stm32工程创建
(2)服务器配置对应变量
(3)单片机打包(上传变量):
(4)单片机拆包(解析下发变量):
文章目录
一. stm32工程创建
(1)stm32工程创建:
1.打开keil5软件,
点击 project-> New Uvison project
2.然后桌面随便建立一个文件夹, 随便起一个名字,不要有空格
3.选择芯片 ,这里用的是 stm32f103c8t6 ,点击确定
4.选择启动文件(后续用到那个选哪个),先只选最基本的
5.点击ok , 然后修改一下启动文件的缓冲区大小(onenet会用到),(如果是只读,去文件夹修改其权限属性)
5.工程创建
(1)在keil的工程目录下, 新建文件夹
Source_code (代表源码存放位置)
(2)在Source_code 下新建 User (代表main函数存放位置)
新建Net代表 网络配置存放位置
然后把 网络配置文件,全黏贴到 Net里面
(3)把文件夹加入工程
接着加入, 把联网的五个文件夹路径,分别加入
(4)这时候发现工程,没有User等文件夹, 是因为我们没有添加文件,
(5)可以看到工程已经有了User, 后续再,以此类推,
现在逐步进行
(6)编译器选择 version 5
(2)stm32上传变量确认
示例程序, 上传一个可以修改的
int temp_th (温度阈值)
_Bool led (台灯)
main.c
#include "stm32f10x.h" // Device header
int temp_th; //(温度阈值)
_Bool led; //(台灯)
int main()
{
temp_th = 1;
led = 1;
while(1)
{
}
return 0;
}
二. 服务器配置对应变量
(1)登录onenet官网:产品开发 - OneNET物联网平台 (10086.cn)
(2)产品开发 ->创建产品
(3)其他行业
(4)设备接入等信息
(5)创建产品属性:产品开发->设置物模型->添加自定义功能节点
(6)根据上传变量进行填写
温度阈值:
台灯:
(7)保存
(8)创建产品
(1)产品id:EeFC4dZQBP
(2)设备名称:d1
(3)设备秘钥:(设备管理->设备->详情->设备秘钥)
YzN6UlBJTXg0MndmbTRQUlBCZ0F0U1A3RnA2aEJ3bkk=
三. 单片机打包(上传变量):
(1) 配置 Uart串口工具
1.添加文件
2.增加库函数
3.引入uart.h
初始化:
(2)初始化wifi:ESP32-01s
1.引入wifi文件,
2.导入
定义
#define ESP8266_ONENET_INFO "AT+CIPSTART=\"TCP\",\"mqtts.heclouds.com\",1883\r\n"
3.wifi串口初始化
Delay_Init();//滴答定时器初始化
uart1_init(115200);//串口1,调试用
uart2_Init(115200); //串口2,驱动ESP8266用
ESP8266_Init();//wifi初始化
4.wifi配置
5.发现文件里都有delay.c
user里面添加delay.c delay.h
delay.h
#ifndef _DELAY_H_
#define _DELAY_H_
#include "stm32f10x.h"
void Delay_Init(void);
void DelayUs(unsigned short us);
void DelayXms(unsigned short ms);
void DelayMs(unsigned short ms);
void delay(uint32_t t);
void delay_us(u32 us);
void delay_ms(u32 ms);
#endif
delay.c
/**
************************************************************
************************************************************
************************************************************
* 文件名: delay.c
*
* 作者: 张继瑞
*
* 日期: 2016-11-23
*
* 版本: V1.0
*
* 说明: 利用systick做阻塞式延时
*
* 修改记录:
************************************************************
************************************************************
************************************************************
**/
//单片机头文件
#include "stm32f10x.h"
//delay头文件
#include "delay.h"
//延时系数
unsigned char UsCount = 0;
unsigned short MsCount = 0;
/*
************************************************************
* 函数名称: Delay_Init
*
* 函数功能: systick初始化
*
* 入口参数: 无
*
* 返回参数: 无
*
* 说明:
************************************************************
*/
void Delay_Init(void)
{
SysTick->CTRL &= ~(1 << 2); //选择时钟为HCLK(72MHz)/8 103--9MHz
UsCount = 9; //微秒级延时系数
MsCount = UsCount * 1000; //毫秒级延时系数
}
/*
************************************************************
* 函数名称: DelayUs
*
* 函数功能: 微秒级延时
*
* 入口参数: us:延时的时长
*
* 返回参数: 无
*
* 说明: 此时钟(21MHz)最大延时798915us
************************************************************
*/
void DelayUs(unsigned short us)
{
unsigned int ctrlResult = 0;
us &= 0x00FFFFFF; //取低24位
SysTick->LOAD = us * UsCount; //装载数据
SysTick->VAL = 0;
SysTick->CTRL = 1; //使能倒计数器
do
{
ctrlResult = SysTick->CTRL;
}
while((ctrlResult & 0x01) && !(ctrlResult & (1 << 16))); //保证在运行、检查是否倒计数到0
SysTick->CTRL = 0; //关闭倒计数器
SysTick->VAL = 0;
}
/*
************************************************************
* 函数名称: DelayXms
*
* 函数功能: 毫秒级延时
*
* 入口参数: ms:延时的时长
*
* 返回参数: 无
*
* 说明:
************************************************************
*/
void DelayXms(unsigned short ms)
{
unsigned int ctrlResult = 0;
if(ms == 0)
return;
ms &= 0x00FFFFFF; //取低24位
SysTick->LOAD = ms * MsCount; //装载数据
SysTick->VAL = 0;
SysTick->CTRL = 1; //使能倒计数器
do
{
ctrlResult = SysTick->CTRL;
}
while((ctrlResult & 0x01) && !(ctrlResult & (1 << 16))); //保证在运行、检查是否倒计数到0
SysTick->CTRL = 0; //关闭倒计数器
SysTick->VAL = 0;
}
/*
************************************************************
* 函数名称: DelayMs
*
* 函数功能: 微秒级长延时
*
* 入口参数: ms:延时的时长
*
* 返回参数: 无
*
* 说明: 多次调用DelayXms,做到长延时
************************************************************
*/
void DelayMs(unsigned short ms)
{
unsigned char repeat = 0;
unsigned short remain = 0;
repeat = ms / 500;
remain = ms % 500;
while(repeat)
{
DelayXms(500);
repeat--;
}
if(remain)
DelayXms(remain);
}
void delay(uint32_t t)
{
while(t--);
}
void delay_us(u32 us)//微秒
{
SysTick_Config(72);
while(us-->0)
{
while(!((SysTick->CTRL)&(1<<16)));
}
SysTick ->CTRL&=~SysTick_CTRL_ENABLE_Msk;
}
void delay_ms(u32 ms)//毫秒
{
SysTick_Config(72000);
while(ms-->0)
{
while(!((SysTick->CTRL)&(1<<16)));
}
SysTick ->CTRL&=~SysTick_CTRL_ENABLE_Msk;
}
-
发现delay.c里面有中断, 加入中断的库函数
(3)配置onenet联网文件
wifi已经配置成功,下面配置链接onenet服务器
1.导入onenet配置文件
- main.c主函数里面, 链接onetnet
while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT"))
DelayXms(500);
while(OneNet_DevLink()) //接入OneNET
DelayXms(500);
OneNET_Subscribe(); //解析onenet下发指令
-
要想链接成功, onenet里面的服务器配置也要对应
-
进入onenet.c, 定义的属性, 进行替换
服务器信息(ctrl 加鼠标左键,快速跳转)
4.回到主函数, 进行上传变量
定义发送间隔
unsigned short timeCount = 0; //发送间隔变量
main.c主函数, 定时上传, 延时10ms, 100次就是1s
72Mhz = 7200,0000次,(1s)
10ms,就是 7200,00次, 换算十六进制为 0xAFC80
//打包上传
if(++timeCount >= 100)
{
OneNet_SendData();
timeCount = 0;
ESP8266_Clear();
}
delay(0xAFC80);//延时10ms
5.先编译运行,鼠标按下f12, 跳转OneNet_SendData();
6.就会来到上传变量的位置
7.引入上传变量
8.严格按照下面格式进行填写:
9.强调:
最后一项,后面没逗号, 之前都有逗号
代码摘录:
extern int temp_th; extern _Bool led; unsigned char OneNet_FillBuf(char *buf) { char text[48]; memset(text, 0, sizeof(text)); strcpy(buf, "{\"id\":\"123\",\"params\":{"); memset(text, 0, sizeof(text)); sprintf(text, "\"temp_th\":{\"value\":%d},", temp_th); strcat(buf, text); memset(text, 0, sizeof(text)); sprintf(text, "\"led\":{\"value\":%s}", led ? "true" : "false"); strcat(buf, text); strcat(buf, "}}"); return strlen(buf); }
四. 单片机拆包(解析下发变量):
(1)检测是否有下发信息
定义接受判断变量
unsigned char *dataPtr = NULL; //标志位, 是否接受到onenet发送的数据
代码摘录:
//检测是否有下发信息
dataPtr = ESP8266_GetIPD(0);
if(dataPtr != NULL)
{
OneNet_RevPro(dataPtr);
//UsartPrintf(USART_DEBUG, "6666666666\r\n");
}
(2)配置CJSON拆包
1.编译,运行 ,然后f12跳转OneNet_RevPro(dataPtr);
2.这里就是接受数据包的函数
我们在这里利用cjson拆包
3.在此函数内,定义拆包所需要的cjson变量
4.①接下来解析原始字符串数据到raw_jason
raw_jason = cJSON_Parse(req_payload);//解析原始字符串数据到raw_jason
② 从处理的cjson数据中,从raw_jason中提取params字符段到params_jason
params_jason=cJSON_GetObjectItem(raw_jason,"params");
5.然后再从params_jason中提取要操作的器件,
temp_th_jason = cJSON_GetObjectItem(params_jason,"temp_th");
led_jason =cJSON_GetObjectItem(params_jason,"led");
6.判断对应cjson 标识符变量 是否为空, 不空,则代表有对应的下发信息
布尔类型, 需要对照其变量
//判断对应cjson 标识符变量 是否为空, 不空,则代表有对应的下发信息
if(temp_th_jason != NULL)
{
temp_th = temp_th_jason->valueint;
}
if(led_jason != NULL)
{
if(led_jason->type == cJSON_True)
{
led = 1;
}
else
{
led = 0;
}
}
7.末尾释放解析字符串空间
cJSON_Delete(raw_jason);
此函数代码备份:
void OneNet_RevPro(unsigned char *cmd)
{
char *req_payload = NULL;
char *cmdid_topic = NULL;
unsigned short topic_len = 0;
unsigned short req_len = 0;
unsigned char qos = 0;
static unsigned short pkt_id = 0;
unsigned char type = 0;
short result = 0;
//原始数据 -> cjson数据
cJSON *raw_jason;
// cjson中摘出标识符变量
cJSON *params_jason;
//这里定义 cjson格式的变量名,例如 *led_jason
cJSON *led_jason,*temp_th_jason;
type = MQTT_UnPacketRecv(cmd);
//UsartPrintf(USART_DEBUG, "688\r\n");
switch(type)
{
case MQTT_PKT_PUBLISH: //接收的Publish消息
// UsartPrintf(USART_DEBUG, "689\r\n");
result = MQTT_UnPacketPublish(cmd, &cmdid_topic, &topic_len, &req_payload, &req_len, &qos, &pkt_id);
if(result == 0)
{
UsartPrintf(USART_DEBUG, "topic: %s, topic_len: %d, payload: %s, payload_len: %d\r\n",
cmdid_topic, topic_len, req_payload, req_len);
//将原始字符串信息,转化成cjson格式
raw_jason = cJSON_Parse(req_payload);//解析原始字符串数据到raw_jason
//从raw_jason中提取params字符段到params_jason
params_jason = cJSON_GetObjectItem(raw_jason,"params");
//然后再从params_jason中提取要操作的器件,
temp_th_jason = cJSON_GetObjectItem(params_jason,"temp_th");
led_jason = cJSON_GetObjectItem(params_jason,"led");
//判断对应cjson 标识符变量 是否为空, 不空,则代表有对应的下发信息
if(temp_th_jason != NULL)
{
temp_th = temp_th_jason->valueint;
}
if(led_jason != NULL)
{
if(led_jason->type == cJSON_True)
{
led = 1;
}
else
{
led = 0;
}
}
cJSON_Delete(raw_jason);
}
case MQTT_PKT_PUBACK: //发送Publish消息,平台回复的Ack
// UsartPrintf(USART_DEBUG, "111\r\n");
if(MQTT_UnPacketPublishAck(cmd) == 0)
// UsartPrintf(USART_DEBUG, "Tips: MQTT Publish Send OK\r\n");
break;
case MQTT_PKT_SUBACK: //发送Subscribe消息的Ack
// UsartPrintf(USART_DEBUG, "222\r\n");
if(MQTT_UnPacketSubscribe(cmd) == 0)
UsartPrintf(USART_DEBUG, "Tips: MQTT Subscribe OK\r\n");
else
UsartPrintf(USART_DEBUG, "Tips: MQTT Subscribe Err\r\n");
break;
default:
// UsartPrintf(USART_DEBUG, "333\r\n");
result = -1;
break;
}
ESP8266_Clear(); //清空缓存
if(result == -1)
return;
if(type == MQTT_PKT_CMD || type == MQTT_PKT_PUBLISH)
{
MQTT_FreeBuffer(cmdid_topic);
MQTT_FreeBuffer(req_payload);
}
}
五.测试
1.发现10个错误, 原来是mqtt的文件没有加入, 在品字设置里面, 把mqtt的所有文件加入,再次编译
2.编译运行, 没错, 烧录设置为st-link
3.按照下图勾选
4.确定, 然后烧录stm32最小系统板子
5.esp32连线方法 , esp32上 RX 链接 PA2 ,TX链接PA3
GND–单片机gnd, vcc–单片机3.3v
6.手机wifi热点设置为 配置的
跳转单片机wifi设置(ctrl+鼠标左键跳转)
7.观察服务器设备在线情况
8.模拟下发设备信息,再次观察属性是否被修改,并上传
9.观察到, 台灯 0, 代表上传下发成功
10.至此, 已经全部演示完毕, 后续的文件,是关于此过程的详细解析 和对应 app的开发及其思路