收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事it行业的老鸟或是对it行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
2. 创建iot服务器端产品
需要先创建产品、在产品下再创建设备。产品是一个大框架,产品下的设备可以有很多,在应用层,可以通过华为云平台提供的api创建设备,删除设备,查询设备属性,在做产品时,软件端可以做一个设备注册的引导界面,完成产品下的设备注册,再将数据传递给设备端,这个过程叫“配网“,具体逻辑需要配合设备端完成。最终完成自动化设备创建,注册,上线等操作。
下面先介绍如何手动创建产品,创建设备,了解创建产品创建设备的过程中需要填充什么参数,理解之后,再使用api时才更加理解参数含义。
2.1 创建产品
直接打开物联网产品页面:
打开产品页面,选择右上角创建产品。
根据自己情况填写信息。就是填写自己产品的一些参数信息。
创建成功后打开产品详情页面,拉到最下面,点击创建自定义模型文件。
这里创建模型文件主要就是为了mqtt客户端能够正确的上传传感器数据上来,每个传感器设置一个属性,这个属性就是表示了传感器的数据值类型。
比如: 先添加一个电机,这个电机就是浇水电机,能上报开关状态,云端也能下发命令控制电机,所以需要添加属性和下发的命令。
添加属性:
添加命令: 因为电机需要云端远程控制。
接下来就创建温度、湿度、光照度传感器的属性,这些传感器只是向云端上传数据,不需要下发指令控制,选择只读就可以了,电机要先实现远程浇灌控制,属性就选择读写。
创建完毕效果,一共有4个属性,电机、温度、湿度、光强度:
2.2 创建设备
选择设备页面,注册设备。
创建后保持设备密匙等信息,接下来登录服务器时,生成mqtt账号密匙需要用到这些参数。
当前创建的设备信息如下:
{
"device_id": "61cd1d97078a93029b84e7b6_1126626497",
"secret": "1126626497"
}
2.3 生成mqtt登录账号信息
官微提供的在线小工具:
按照提示填入数据,生成,非常方便。
当前生成的信息如下:
clientid 61cd1d97078a93029b84e7b6_1126626497_0_0_2021123003
username 61cd1d97078a93029b84e7b6_1126626497
password b219f3a0099fa0284a2671a5c699b67a7cf6d5f7355d9ee8190011f3b64f71b5
3. 使用mqtt客户端模拟测试
为了验证服务器配置是否ok,先使用mqtt客户端软件进行连接测试。
3.1 华为云iot服务器地址与端口
端口: 1883
域名: a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
ip地址: 121.36.42.100
3.2 订阅主题
在产品页面,可以看到主题管理页面,能看到当前设备可以订阅的主题有哪些。
一般订阅下发的数据:
格式: $oc/devices/{device_id}/sys/messages/down
//订阅主题: 平台下发消息给设备
$oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/messages/down
3.3 上报主题数据
官方文档介绍:
服务id,属性id在产品页面查看,2.1小节创建产品里就讲了这个属性的作用。
每次可以单个属性上报,也可以一起上报。
格式: $oc/devices/{device_id}/sys/properties/report
//设备上报主题请求
$oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/properties/report
//上报的数据格式如下
//电机开状态反馈
{"services": [{"service_id": "motor","properties":{"motor":1}}]}
//电机关状态反馈
{"services": [{"service_id": "motor","properties":{"motor":0}}]}
//温度上报
{"services": [{"service_id": "motor","properties":{"sht30_h":14}}]}
//湿度上报
{"services": [{"service_id": "motor","properties":{"sht30_l":70}}]}
//光照强度上报
{"services": [{"service_id": "motor","properties":{"bh1750":80}}]}
//也可以一起上报
{"services": [{"service_id": "motor","properties":{"motor":1}},{"service_id": "motor","properties":{"sht30_h":15}},{"service_id": "motor","properties":{"sht30_l":70}},{"service_id": "motor","properties":{"bh1750":80}}]}
3.4 登录服务器
按照软件提示,填入相关数据即可。
如需要也需要使用和我一样的同款软件,打开百度搜索mqtt客户端_v2.4(协议3.1.1).exe 即可找到下载地址。
发送数据后查看云端,已经登录成功,数据已经上传成功。
3.5 下发命令
电机设备支持读写,支持下发命令,在设备页面测试。
点击确定之后,参看mqtt客户端软件,已经收到了下发的数据。
len:174,data:l$oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/commands/request_id=390ce15d-6e69-4021-b83a-5e953eea874c{"paras":{"motor":1},"service_id":"motor","command_name":"motor"}
4. 设备端上华为云iot
4.1 安装keil软件
mcu采用的stm32芯片,设备端代码编写开发就采用的keil5。
keil5安装包下载地址:
安装keil时,软件要放在英文目录下,电脑的用户名必须是英文,否则会出现一些奇怪问题。
安装过程中,根据提示下一步下一步点击即可。
4.2 编写代码
工程代码:
工程代码较多,这里就贴出main.c全部代码:
#include "main.h"
#include "stm32l4xx_hal.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include "e53_ia1.h"
#include "lcd.h"
#include "spi.h"
#include "mqtt.h"
#include "esp8266.h"
/* user code begin includes */
#include "stdio.h"
/* user code end includes */
void systemclock_config(void);
#define esp8266_wifi_ap_ssid "cmcc-cqvn" //将要连接的路由器名称 --不要出现中文、空格等特殊字符
#define esp8266_ap_password "99pu58cb" //将要连接的路由器密码
//华为云iot物联网服务器的设备信息
#define mqtt_clientid "61cd1d97078a93029b84e7b6_1126626497_0_0_2021123003"
#define mqtt_username "61cd1d97078a93029b84e7b6_1126626497"
#define mqtt_password "b219f3a0099fa0284a2671a5c699b67a7cf6d5f7355d9ee8190011f3b64f71b5"
//订阅与发布的主题
#define set_topic "$oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/messages/down" //订阅
#define post_topic "$oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/properties/report" //发布
//保存温湿度、光照强度
e53_ia1_data_typedef e53_ia1_data;
//显示文本
char lcd_text_str[50];
uart_handletypedef at_usart;
//低功耗串口初始化
int32_t at_usart_init(void)
{
at_usart.instance = lpuart1;
at_usart.init.baudrate = 115200;
at_usart.init.wordlength = uart_wordlength_8b;
at_usart.init.stopbits = uart_stopbits_1;
at_usart.init.parity = uart_parity_none;
at_usart.init.hwflowctl = uart_hwcontrol_none;
at_usart.init.mode = uart_mode_rx | uart_mode_tx;
if(hal_uart_init(&at_usart) != hal_ok)
{
_error_handler(__file__, __line__);
}
// __hal_uart_clear_flag(usart, uart_flag_tc);
__hal_uart_enable_it(&at_usart, uart_it_idle);
__hal_uart_enable_it(&at_usart, uart_it_rxne);
hal_nvic_enableirq(lpuart1_irqn); //使能usart1中断通道
hal_nvic_setpriority(lpuart1_irqn, 3, 3); //抢占优先级3,子优先级3
return 0;
}
unsigned char esp8266_recvbuf[max_recv_cnt];
unsigned int esp8266_recv_cnt=0;
unsigned int esp8266_recv_flag=0;
void lpuart1_irqhandler()
{
//接收到数据
if(__hal_uart_get_flag(&at_usart, uart_flag_rxne) != reset)
{
if(esp8266_recv_cnt<max_recv_cnt-1)
{
esp8266_recvbuf[esp8266_recv_cnt++] = (uint8_t)(at_usart.instance->rdr & 0x00ff);
}
else
{
esp8266_recv_flag=1;
}
}
else if (__hal_uart_get_flag(&at_usart, uart_flag_idle) != reset)
{
__hal_uart_clear_idleflag(&at_usart);
esp8266_recv_flag=1;
}
}
void at_senddata(unsigned char *p,unsigned int len)
{
int i=0;
for(i=0;i<len;i++)
{
while((lpuart1->isr & 0x40) == 0); //循环发送,直到发送完毕
lpuart1->tdr = p[i];
}
}
char mqtt_message[200];
int main(void)
{
int i=0;
int cnt=0;
int motor_state=0;
hal_init();
systemclock_config();
mx_gpio_init();
mx_i2c1_init();
mx_spi2_init();
mx_usart1_uart_init();
at_usart_init();
//初始化硬件
init_e53_ia1();
lcd_init();
lcd_clear(black);//清屏为黑色
lcd_showstring(0, 00, 240, 32, 32, "init esp8266");//显示字符串,字体大小32*32
if(esp8266_init())
{
printf("esp8266硬件检测错误.\n");
lcd_clear(black);//清屏为黑色
lcd_showstring(0, 00, 240, 32, 32, "esp8266 error");//显示字符串,字体大小32*32
}
else
{
lcd_clear(black);//清屏为黑色
lcd_showstring(0, 00, 240, 32, 32, "esp8266 ok");//显示字符串,字体大小32*32
printf("准备连接到指定的服务器.\n");
//非加密端口
printf("wifi:%d\r\n",esp8266_sta_tcp_client_mode(esp8266_wifi_ap_ssid,esp8266_ap_password,"106.55.124.154",1883,1));
}
//2. mqtt协议初始化
mqtt_init();
//3. 连接华为云iot服务器
while(mqtt_connect(mqtt_clientid,mqtt_username,mqtt_password))
{
printf("服务器连接失败,正在重试...\n");
hal_delay(500);
}
printf("服务器连接成功.\n");
//3. 订阅主题
if(mqtt_subscribetopic(set_topic,0,1))
{
printf("主题订阅失败.\n");
}
else
{
printf("主题订阅成功.\n");
}
while (1)
{
if(hal_gpio_readpin(key1_gpio_port,key1_pin)==gpio_pin_reset)//查询按键key1低电平
{
hal_delay(10);//消抖
if(hal_gpio_readpin(key1_gpio_port,key1_pin)==gpio_pin_reset)//查询按键key1低电平
{
hal_gpio_writepin(led_gpio_port,led_pin,gpio_pin_set);//亮
//补光灯亮
hal_gpio_writepin(ia1_light_gpio_port, ia1_light_pin, gpio_pin_set);
//电机转
hal_gpio_writepin(ia1_motor_gpio_port, ia1_motor_pin, gpio_pin_set);
motor_state=1;
}
}
if(hal_gpio_readpin(key2_gpio_port,key2_pin)==gpio_pin_reset)//查询按键key2低电平
{
hal_delay(10);//消抖
if(hal_gpio_readpin(key2_gpio_port,key2_pin)==gpio_pin_reset)//查询按键key2低电平
{
hal_gpio_writepin(led_gpio_port,led_pin,gpio_pin_reset);//灭
//补光灯灭
hal_gpio_writepin(ia1_light_gpio_port, ia1_light_pin, gpio_pin_reset);
//电机停
hal_gpio_writepin(ia1_motor_gpio_port, ia1_motor_pin, gpio_pin_reset);
motor_state=0;
}
}
cnt++;
hal_delay(10);
if(cnt>=100)
{
cnt=0;
e53_ia1_read_data();
printf("光照强度:%d %%\r\n", (int)e53_ia1_data.lux);
printf("湿度:%d %%\r\n",(int)e53_ia1_data.humidity);
printf("温度:%d ℃\r\n", (int)e53_ia1_data.temperature);
sprintf(lcd_text_str,"l: %d %%",(int)e53_ia1_data.lux);
lcd_showstring(40, 50+10+32*1, 240, 32, 32,lcd_text_str);
sprintf(lcd_text_str,"h: %d %%",(int)e53_ia1_data.humidity);
lcd_showstring(40, 50+10+32*2, 240, 32, 32,lcd_text_str);
sprintf(lcd_text_str,"t: %d c",(int)e53_ia1_data.temperature);
lcd_showstring(40, 50+10+32*3, 240, 32, 32,lcd_text_str);
//切换引脚的状态
hal_gpio_togglepin(led_gpio_port,led_pin);
//上传数据
sprintf(mqtt_message,"{\"services\": [{\"service_id\": \"motor\",\"properties\":{\"motor\":%d}},"
"{\"service_id\": \"motor\",\"properties\":{\"sht30_h\":%d}},{\"service_id\": \"motor\",\"properties\":"
"{\"sht30_l\":%d}},{\"service_id\": \"motor\",\"properties\":{\"bh1750\":%d}}]}",
motor_state,(int)e53_ia1_data.humidity,(int)e53_ia1_data.temperature,(int)e53_ia1_data.lux);
mqtt_publishdata(post_topic,mqtt_message,0);
//根据湿度自动灌溉
if((int)e53_ia1_data.humidity<50) //小于50自动灌溉
{
printf("自动灌溉....\n");
motor_state=1; //电机状态更新
//电机转
hal_gpio_writepin(ia1_motor_gpio_port, ia1_motor_pin, gpio_pin_set);
}
}
//接收到数据
if(esp8266_recv_flag)
{
//如果是下发了属性,判断是开锁还是关锁
if(esp8266_recv_cnt>5)
{
esp8266_recvbuf[esp8266_recv_cnt]='\0';
//使用字符串查找函数
if(strstr((char*)&esp8266_recvbuf[5],"\"machine\":1"))
{
motor_state=1; //电机状态更新
//电机转
hal_gpio_writepin(ia1_motor_gpio_port, ia1_motor_pin, gpio_pin_set);
printf("开启电机...\n");
}
else if(strstr((char*)&esp8266_recvbuf[5],"\"machine\":0"))
{
//电机停
hal_gpio_writepin(ia1_motor_gpio_port, ia1_motor_pin, gpio_pin_reset);
motor_state=0;
printf("关闭电机...\n");
}
for(i=0;i<esp8266_recv_cnt;i++)printf("%c",esp8266_recvbuf[i]);
esp8266_recv_cnt=0;
}
esp8266_recv_flag=0;
}
}
}
void systemclock_config(void)
{
rcc_oscinittypedef rcc_oscinitstruct;
rcc_clkinittypedef rcc_clkinitstruct;
rcc_periphclkinittypedef periphclkinit;
/**initializes the cpu, ahb and apb busses clocks
*/
rcc_oscinitstruct.oscillatortype = rcc_oscillatortype_hsi|rcc_oscillatortype_msi;
rcc_oscinitstruct.hsistate = rcc_hsi_on;
rcc_oscinitstruct.hsicalibrationvalue = 16;
rcc_oscinitstruct.msistate = rcc_msi_on;
rcc_oscinitstruct.msicalibrationvalue = 0;
rcc_oscinitstruct.msiclockrange = rcc_msirange_6;
rcc_oscinitstruct.pll.pllstate = rcc_pll_on;
rcc_oscinitstruct.pll.pllsource = rcc_pllsource_msi;
rcc_oscinitstruct.pll.pllm = 1;
rcc_oscinitstruct.pll.plln = 40;
rcc_oscinitstruct.pll.pllp = rcc_pllp_div7;
rcc_oscinitstruct.pll.pllq = rcc_pllq_div2;
rcc_oscinitstruct.pll.pllr = rcc_pllr_div2;
if (hal_rcc_oscconfig(&rcc_oscinitstruct) != hal_ok)
{
_error_handler(__file__, __line__);
}
/**initializes the cpu, ahb and apb busses clocks
*/
rcc_clkinitstruct.clocktype = rcc_clocktype_hclk|rcc_clocktype_sysclk
|rcc_clocktype_pclk1|rcc_clocktype_pclk2;
rcc_clkinitstruct.sysclksource = rcc_sysclksource_pllclk;
rcc_clkinitstruct.ahbclkdivider = rcc_sysclk_div1;
rcc_clkinitstruct.apb1clkdivider = rcc_hclk_div1;
rcc_clkinitstruct.apb2clkdivider = rcc_hclk_div1;
if (hal_rcc_clockconfig(&rcc_clkinitstruct, flash_latency_4) != hal_ok)
{
_error_handler(__file__, __line__);
}
periphclkinit.periphclockselection = rcc_periphclk_usart1|rcc_periphclk_i2c1;
periphclkinit.usart1clockselection = rcc_usart1clksource_pclk2;
periphclkinit.i2c1clockselection = rcc_i2c1clksource_hsi;
if (hal_rccex_periphclkconfig(&periphclkinit) != hal_ok)
{
_error_handler(__file__, __line__);
}
/**configure the main internal regulator output voltage
*/
if (hal_pwrex_controlvoltagescaling(pwr_regulator_voltage_scale1) != hal_ok)
{
_error_handler(__file__, __line__);
}
/**configure the systick interrupt time
*/
hal_systick_config(hal_rcc_gethclkfreq()/1000);
/**configure the systick
*/
hal_systick_clksourceconfig(systick_clksource_hclk);
/* systick_irqn interrupt configuration */
hal_nvic_setpriority(systick_irqn, 0, 0);
}
/* user code begin 4 */
/* user code end 4 */
/**
* @brief this function is executed in case of error occurrence.
* @param file: the file name as string.
* @param line: the line in file as a number.
* @retval none
*/
void _error_handler(char *file, int line)
{
/* user code begin error_handler_debug */
/* user can add his own implementation to report the hal error return state */
while(1)
{
}
/* user code end error_handler_debug */
}
#ifdef use_full_assert
/**
* @brief reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval none
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* user code begin 6 */
/* user can add his own implementation to report the file name and line number,
tex: printf("wrong parameters value: file %s on line %d\r\n", file, line) */
/* user code end 6 */
}
#endif /* use_full_assert */
5. 上位机软件开发
上位机与设备之间通信,需要通过服务器完成,服务器提供了对应的api接口,所以对于上位机而言通信主要是对http请求进行处理,返回的数据进行解析等操作。
当前的软件采用是采用qt设计的,实现了产品注册、设备注册、获取在线设备,获取设备属性,远程指令发送等主要功能。
访问华为云的接口都需要填一个x-auth-token参数,这个参数获取需要iam账号,下面第一步就先介绍,如何创建iam账号,如何获取x-auth-token参数。
5.1 创建iam账户
创建一个iam账户,方便接下来使用api接口访问华为云服务时,生成token登录密匙。
地址:
账户创建好之后,代码里就可以编写一个获取token的函数。
/*
功能: 获取token
*/
void widget::gettoken()


**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**
g)




账户创建好之后,代码里就可以编写一个获取token的函数。
/*
功能: 获取token
*/
void widget::gettoken()
[外链图片转存中…(img-tphjhtci-1715662682894)]
[外链图片转存中…(img-nqd9l43e-1715662682896)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新
发表评论