admin管理员组文章数量:1603256
饮水机加入Onenet联网代码实操
引言
饮水机联网调试变量解析
在上一讲, 我们已经分析了 饮水机各个功能的调试方法, 并且确定了需要调试的变量, 这次我们就进行Onenet的代码实操, 加入联网工程代码, 主要是走个流程.
我们加入联网, 主要是把这些变量,上传到服务器, 让我们可视化的实时的观察单片机的内部变量状态。
把变量上传到服务器实时观察, 就取代了oled在屏幕上, 观察变量
同时我们要修改其中的变量, 所以我们就要在服务器下发修改变量的值, 然后单片机拆包解析后, 赋予本地变量。
服务器下发指令, 就代替了, 调试时候的赋予变量和按键赋值, 更具有灵活性
加入工程, 参考示例步骤:
手把手stm32与Onenet联网配置步骤
上面的示例步骤是物联网预警系统的, 我们只是变量不同罢了, 所以只需要更改相关的标识符就可以了.
所以我们比葫芦画瓢就可以了.在掌握了上面的联网工程后, 后面就会很简单, 我们直接忽略详细步骤, 只讲 饮水机和预警系统, 赋值的不同的变量的部分, 站在已经能够掌握整个流程的角度来, 如何快速复制黏贴.
本项目调试好的工程代码(后面是从头开始操作,这是成品):
跳转下载
https://wwyz.lanzoul/i3Wn728iriji
本地工程加入联网
无联网本地饮水机工程
点击跳转下载
https://wwyz.lanzoul/i6fZu28he71a
饮水机联网工程文件
点击跳转下载
回去(ctrl 加鼠标左键,快速跳转)
https://wwyz.lanzoul/ix8Ys28heg4h
需要上传的变量整理
加热系统
回去(ctrl 加鼠标左键,快速跳转)
需要上传
//水温 water_temp //加热阈值 heat_temp_th //保温阈值 KeepWarm_th //加热模式 heat_mode
饮水系统
我们根据饮水机的童锁和红外的交互功能, 来判断, 是否可以控制底层开启水龙头
上图, 描述了他们之间的关系, 代码中也体现了出来, 所以我们上传的变量分别是
//童锁状态 child_lock_info.Status //杯子是否放置 cup_place_info.Status //水龙头出水状态 out_water_info.Status
Onenet服务器产品创建
1.官网
OneNET - 中国移动物联网开放平台 (10086)
2.登录注册
注意, 密码的特殊符号是 @ 其他标点不行
3.进入Onenet产品开发
如果找不到, 就直接把这个链接放在导航栏直接回车
产品开发 - OneNET物联网平台 (10086)
https://open.iot.10086/console/product/own
4.点击创建产品
5.产品分类->其他行业->其他行业->其他类别
6.产品名称:饮水机示例
所属城市:随便填
节点类型: 直连设备
接入协议: MQTT
数据协议: OneJson
联网方式: wifi
开发方案: 自定义方案
7.根据所需变量设置对应的物模型
8.变量对应的物模型
本地变量(ctrl 加鼠标左键,快速跳转)
一般情况下, 服务器物模型标识符可以设置成和本地一样, 这样方便, 但是如果我们使用结构体的话, 直接使用会造成变量太长, 所以我们可以换其他简短的意思一样的变量就行, 相当于取了一个代号, 到时候服务器下发控制变量也会按照这个标识符进行下发, 我们单片机解析的时候,记得按照Onenet设置的这个简短标识符进行拆包解析就行了.
<1>水温
物模型标识符 water_temp
对应本地变量 water_temp
数据类型: int32(整数型)(onenet整数类型都是这个)
取值范围: 0-100
步长: 1
单位:摄氏度
读写类型: 读写 (可读可写方便一会儿我们进行模拟上帝调试, 实际使用切勿可写, 需要真正从器件读取水温)
<2>加热阈值
物模型标识符 heat_temp_th
对应本地变量 heat_temp_th
数据类型: int32(整数型)(onenet整数类型都是这个)
取值范围: 0-100
步长: 1
单位:摄氏度
读写类型: 读写 (可读可写方便一会儿我们进行模拟上帝调试, 同时后续方便用户设置阈值)
<3>保温阈值
物模型标识符 Keepwarm_th
对应本地变量 Keepwarm_th
数据类型: int32(整数型)(onenet整数类型都是这个)
取值范围: 0-100
步长: 1
单位:摄氏度
读写类型: 读写 (可读可写方便一会儿我们进行模拟上帝调试, 同时后续方便用户设置阈值)
<4>加热模式
物模型标识符 heat_mode
对应本地变量 heat_mode
数据类型: int32(整数型)(onenet整数类型都是这个)
取值范围: 0-2
步长: 1
单位:个
读写类型: 读写 (可读可写方便一会儿我们进行模拟上帝调试, 同时后续方便用户设置模式)
<5> 童锁状态
物模型标识符 children_lock
对应本地变量 child_lock_info.Status
数据类型: bool(布尔)
布尔值 true - 1 (童锁锁住)
false - 0 (童锁解开)
读写类型: 读写 (可读可写方便一会儿我们进行模拟上帝调试, 同时后续方便用户设置童锁)
<6>杯子是否放置 (红外检测)
物模型标识符 cup_place
对应本地变量 cup_place_info.Status
数据类型: bool(布尔)
布尔值 true - 1 (放置了杯子)
false - 0 (未检测到杯子)
读写类型: 读写 (可读可写方便一会儿我们进行模拟上帝调试, 同时后续方便模拟杯子放上,优化本地逻辑)
<7> 水龙头出水状态
物模型标识符 out_water
对应本地变量 out_water_info.Status
数据类型: bool(布尔)
布尔值 true - 1 (正在放水)
false - 0 (水龙头关闭)
读写类型: 读写 (可读可写方便一会儿我们进行模拟上帝调试, 同时后续方便测试安全措施,优化本地逻辑)
9.创建产品
回去(ctrl 加鼠标左键,快速跳转)
(1)产品id:A9g7LOO1B3
(2)设备名称:d1
(3)设备秘钥:(设备管理->设备->详情->设备秘钥)
UGNZQmt2SFFpbFV0NkczMGNTMzh0QW9IUTVpbGp1Skg=
单片机本地联网代码加入
1.代码下载
联网代码下载(ctrl 加鼠标左键,快速跳转)
下载完联网功能代码, 解压
2.把Net文件夹复制加入Source_code源代码文件夹内
3.把此文件夹内的所有类别文件夹, 加入工程
我们首先打开工程
4.点击品字, 然后添加联网文件夹内的工程名字
cjson
mqtt
onenet
uart
wifi
5.逐个把对应文件夹的文件加入
点击对应的Group,再点击Add Files,然后选中本地对应的文件夹, 把文件类型选为all , 然后鼠标选中所有文件点击add
6.接着把这些文件夹路径加入
7.这样就可以看到工程左侧已经添加了文件
联网代码初始化
wifi:ESP32-01S初始化
1.main.c引入 wifi头文件
#include "esp8266.h"
定义
#define ESP8266_ONENET_INFO "AT+CIPSTART=\"TCP\",\"mqtts.heclouds\",1883\r\n"
2.wifi串口初始化
Delay_Init();//滴答定时器初始化
uart1_init(115200);//串口1,调试用
uart2_Init(115200); //串口2,驱动ESP8266用
ESP8266_Init();//wifi初始化
3.wifi配置
回去(ctrl 加鼠标左键,快速跳转)
4.发现文件里都有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;
}
5.发现delay.c里面有中断, 加入中断的库函数
配置Onenet联网文件
wifi已经配置成功,下面配置链接onenet服务器
1.刚才导入的onenet配置文件
2.在main.c里面引入 onenet头文件
#include "onenet.h"
3.main.c主函数里面, 链接onetnet
while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT"))
DelayXms(500);
while(OneNet_DevLink()) //接入OneNET
DelayXms(500);
OneNET_Subscribe(); //解析onenet下发指令
4.要想链接成功, onenet里面的服务器配置也要对应
5.进入onenet.c, 定义的属性, 进行替换
服务器信息(ctrl 加鼠标左键,快速跳转)
6.回到主函数, 进行上传变量
定义发送间隔
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
7.先编译运行,鼠标按下f12, 跳转OneNet_SendData();
8.就会来到上传变量的位置
9.引入上传变量
extern int water_temp; //水温
extern int heat_temp_th; //加热阈值
extern int KeepWarm_th; //保温阈值
extern int heat_mode; //加热模式
因为童锁和红外线是定义在其他文件内, 所以我们包含其头文件即可
#include "infrared_cup.h"
#include "out_water.h"
10.严格按照下面格式进行填写:
11…强调:
最后一项,后面没逗号, 之前都有逗号
代码摘录:
extern int water_temp; //水温
extern int heat_temp_th; //加热阈值
extern int KeepWarm_th; //保温阈值
extern int heat_mode; //加热模式
uint16_t OneNet_FillBuf(char *buf)
{
char text[48];
memset(text, 0, sizeof(text));
strcpy(buf, "{\"id\":\"123\",\"params\":{");
memset(text, 0, sizeof(text));
sprintf(text, "\"water_temp\":{\"value\":%d},", water_temp);
strcat(buf, text);
memset(text, 0, sizeof(text));
sprintf(text, "\"heat_temp_th\":{\"value\":%d},", heat_temp_th);
strcat(buf, text);
memset(text, 0, sizeof(text));
sprintf(text, "\"KeepWarm_th\":{\"value\":%d},", KeepWarm_th);
strcat(buf, text);
memset(text, 0, sizeof(text));
sprintf(text, "\"heat_mode\":{\"value\":%d},", heat_mode);
strcat(buf, text);
memset(text, 0, sizeof(text));
sprintf(text, "\"cup_place\":{\"value\":%s},", cup_place_info.Status ? "true" : "false");
strcat(buf, text);
memset(text, 0, sizeof(text));
sprintf(text, "\"out_water\":{\"value\":%s},", out_water_info.Status ? "true" : "false");
strcat(buf, text);
memset(text, 0, sizeof(text));
sprintf(text, "\"children_lock\":{\"value\":%s}", child_lock_info.Status ? "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中提取要操作的饮水机变量
water_temp_jason = cJSON_GetObjectItem(params_jason,"water_temp");
heat_temp_th_jason = cJSON_GetObjectItem(params_jason,"heat_temp_th");
KeepWarm_th_jason = cJSON_GetObjectItem(params_jason,"KeepWarm_th");
heat_mode_jason = cJSON_GetObjectItem(params_jason,"heat_mode");
children_lock_jason = cJSON_GetObjectItem(params_jason,"children_lock");
out_water_jason = cJSON_GetObjectItem(params_jason,"out_water");
cup_place_jason = cJSON_GetObjectItem(params_jason,"cup_place");
6.判断对应cjson 标识符变量 是否为空, 不空,则代表有对应的下发信息
布尔类型, 需要对照其变量
if(out_water_jason != NULL)
{
if(out_water_jason->type == cJSON_True )
{
OUT_water_Set(out_water_ON);
}
else
{
OUT_water_Set(out_water_OFF);
}
}
if(cup_place_jason != NULL)
{
if(cup_place_jason->type == cJSON_True )
{
cup_place_info.Status = Cup_placement;
}
else
{
cup_place_info.Status = Cup_disappears;
}
}
if(children_lock_jason != NULL)
{
if(children_lock_jason->type == cJSON_True )
{
if(child_lock_info.Status != lock_child)
{
child_lock_info.Status = lock_child;
}
}
else
{
if(child_lock_info.Status != Unlock_child)
{
child_lock_info.Status = Unlock_child;
}
}
}
7.末尾释放解析字符串空间
cJSON_Delete(raw_jason);
此函数代码备份:
//==========================================================
// 函数名称: OneNet_RevPro
//
// 函数功能: 平台返回数据检测
//
// 入口参数: dataPtr:平台返回的数据
//
// 返回参数: 无
//
// 说明:
//==========================================================
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 *water_temp_jason;
cJSON *heat_temp_th_jason;
cJSON *KeepWarm_th_jason;
cJSON *heat_mode_jason;
cJSON *children_lock_jason;
cJSON *cup_place_jason;
cJSON *out_water_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中提取要操作的器件,
water_temp_jason = cJSON_GetObjectItem(params_jason,"water_temp");
heat_temp_th_jason = cJSON_GetObjectItem(params_jason,"heat_temp_th");
KeepWarm_th_jason = cJSON_GetObjectItem(params_jason,"KeepWarm_th");
heat_mode_jason = cJSON_GetObjectItem(params_jason,"heat_mode");
children_lock_jason = cJSON_GetObjectItem(params_jason,"children_lock");
out_water_jason = cJSON_GetObjectItem(params_jason,"out_water");
cup_place_jason = cJSON_GetObjectItem(params_jason,"cup_place");
//判断对应cjson 标识符变量 是否为空, 不空,则代表有对应的下发信息
if(water_temp_jason != NULL)
{
water_temp = water_temp_jason->valueint;
}
if(heat_temp_th_jason != NULL)
{
heat_temp_th = heat_temp_th_jason->valueint;
}
if(KeepWarm_th_jason != NULL)
{
KeepWarm_th = KeepWarm_th_jason->valueint;
}
if(heat_mode_jason != NULL)
{
heat_mode = heat_mode_jason->valueint;
}
if(out_water_jason != NULL)
{
if(out_water_jason->type == cJSON_True )
{
OUT_water_Set(out_water_ON);
}
else
{
OUT_water_Set(out_water_OFF);
}
}
if(cup_place_jason != NULL)
{
if(cup_place_jason->type == cJSON_True )
{
cup_place_info.Status = Cup_placement;
}
else
{
cup_place_info.Status = Cup_disappears;
}
}
if(children_lock_jason != NULL)
{
if(children_lock_jason->type == cJSON_True )
{
if(child_lock_info.Status != lock_child)
{
child_lock_info.Status = lock_child;
}
}
else
{
if(child_lock_info.Status != Unlock_child)
{
child_lock_info.Status = Unlock_child;
}
}
}
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.烧录方法
2.esp32连线方法 , esp32上 RX 链接 PA2 ,TX链接PA3
GND–单片机gnd, vcc–单片机3.3v
3.手机wifi热点设置为 配置的
跳转单片机wifi设置(ctrl+鼠标左键跳转)
4.观察服务器设备在线情况
5.模拟下发设备信息,再次观察属性是否被修改,并上传
6.然后就按照我们之前的调试方法, 进行调试
饮水机复杂交互功能联网调试
https://blog.csdn/qq_57484399/article/details/141574662
版权声明:本文标题:饮水机加入Onenet联网代码实操 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dianzi/1728435339a1158058.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论