当前位置: 首页 > news >正文

单片机-STM32部分:18、WiFi模组

飞书文档https://x509p6c8to.feishu.cn/wiki/WFmqwImDViDUezkF7ercZuNDnve

一、WiFi模组应用

当设备需要连接网络,实现远程控制,状态监控时,就需要添加通信模组,常见的通信模组WiFi模组、2G模组、4G模组等:

我们的板卡上没有直接用现成的模组,而是直接把WiFi芯片设计到板卡上:

所以,我们就可以基于这个芯片尝试做联网的应用。

因为我们用的是原厂芯片的方案,所以WiFi芯片内是没有程序的,我们需要先到官方下载WiFi的固件进行烧录,烧录成功后,才能用串口驱动WiFi芯片。

二、WiFi模组固件烧录

AT指令是什么?

ESP-AT 是乐鑫开发的可直接用于量产的物联网应用固件,旨在降低客户开发成本,快速形成产品。通过 ESP-AT 指令,您可以快速加入无线网络、连接云平台、实现数据通信以及远程控制等功能,真正的通过无线通讯实现万物互联。

固件下载

官方AT固件下载(ESP8266是乐鑫的方案)

https://docs.espressif.com/projects/esp-at/en/release-v2.2.0.0_esp8266/AT_Binary_Lists/ESP8266_AT_binaries.html

安信可固件下载(安信可是ESP8266模组厂商)

https://docs.ai-thinker.com/%E5%9B%BA%E4%BB%B6%E6%B1%87%E6%80%BB


 

建议直接下载下方固件即可:参考飞书文档

烧录接线

下载AT指令固件到本地后,我们需要先按下方接好线,DC电源头供电,核心板不接,然后只接TX RX GND

部分小伙伴出现电脑USB供电不足导致烧录失败或乱码、无应答问题,可以尝试独立供电,如下图:

  1. 1、拆除STM32核心板,因为STM32的TX RX和WiFi的共用,避免有数据干扰。
  2. 2、USB转TTL模块只需要接TXD、RXD、GND到板卡,注意3V3不需要接
  3. 3、接上电源适配器,按复位按键即可进入烧录(注意烧录模式短接MODE的插针,运行模式断开)

注意,如果使用外部DC头供电,下方的3.3V红色线就不需要接了。

如果接线正常,我们可以在电脑的设备管理器中,可以看到新出现的COM口,每台电脑不一样,可以查看下自己电脑的COM口,下方烧录需要用到。

烧录软件使用

需要使用乐鑫提供的flash_download_tool工具进行固件烧录,下载链接如下。

https://www.espressif.com.cn/zh-hans/support/download/other-tools

[flash_download_tool_3.9.2_0.zip]

下载后直接解压即用,运行烧录工具中的.exe文件,选择型号,选择ESP8266

打开烧录软件,

从"…“选择要烧录的bin文件: xxxESP8266-AT_MQTT-xM.bin,上面下载的固件。
填写烧录地址"0x0”,然后勾选固件前面的√,很重要;
SPI MODE选择 “DOUT”;
"DoNotChgBin"不变;
“COM” 选择你电脑的COM口;
波特率默认即可。

上述配置完成后, 点击下方START开始烧录,若一直停在等待上电同步,可以点击板卡的RES按钮,就可以开始下载了,下载一般20s左右,如果1S内完成的,一般是上面没勾选,如果长时间没下载完,一般是线有问题,可以换杜邦线重新接稳。

下载完成后,拔掉J10的IO0的杜邦线,从下载模式切换到运行模式,打开串口助手,设置波特率115200 8 N 1,按RES按钮,就可以看到设备启动数据,注意

需要接上电源&打开开关,能看到如下打印

到这里,模组烧录就已经完成啦

注意:

部分小伙伴出现电脑USB供电不足导致烧录失败或乱码问题,可以尝试独立供电,如下图:

  1. 1、拆除STM32核心板,因为STM32的TX RX和WiFi的共用,避免有数据干扰。
  2. 2、USB转TTL模块只需要接TXD、RXD、GND到板卡,注意3V3不需要接
  3. 3、接上电源适配器,按复位按键即可进入烧录(注意烧录模式短接MODE的插针,运行模式断开)

三、阿里云物联网云平台

我们WiFi准备好后,

还需要有个路由器,方便WiFi模组连接外网,也可以开手机热点。

最后,还需要有个云服务器做远程控制,或者上报设备状态。

云服务器厂家有很多

阿里云物联网

腾讯物联网

中移动物联网

机智云等等

注意:

由于近期阿里云暂停个人账号开通物联网平台,为了方便大家做实验,这里开放几个测试账号给大家使用

阿里云物联网共享账号 目前只提供购买板卡小伙伴学习使用,可以从旺旺给你发送的资料链接中获取密码。

共享账号已经创建了产品,大家可以拷贝创建好的设备的MQTT连接参数直接使用,然后直接查看第四章。

课程使用阿里云物联网平台,需要我们注册登录下,下方是步骤

https://www.aliyun.com/

点击管理控制台

如果提示未开通,点击开通即可,测试使用是免费的。

选择公共实例,然后鼠标点击“公共实例”进入。

如果尚未开通,需要先进行开通,开通时间一般5min内

创建一个新的产品。

填写产品名称所属品类等必要信息,并完成产品创建步骤。

这里我们选择创建一个温湿度检测产品,产品将会影响到后续的通信协议字段。

在创建完成的产品页面上,点击添加设备。

输入设备名称并确认。

至此,阿里云物联网平台创建设备完成。进入设备详情页,查看MQTT连接参数,并进行保存。

至此,平台部分配置完成,

这些参数将用于在ESP-01S / ESP8266上配置AT指令,实现设备与物联网平台之间的通信。

{"clientId":"k0rqhbzuJeE.device1|securemode=2,signmethod=hmacsha256,timestamp=1705652029669|","username":"device1&k0rqhbzuJeE","mqttHostUrl":"iot-06z00dcx1hvlwd3.mqtt.iothub.aliyuncs.com","passwd":"f3c17488453a737afd24edefdd4ec6862848a0ca2f8866b7015f5787b90e68a2","port":1883}

四、配置WiFi模组连接云平台

连接阿里云服务器

阿里云使用MQTT协议

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。
MQTT最大优点在于,用极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。
作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。

MQTT 与 HTTP 一样,MQTT 运行在传输控制协议/互联网协议 (TCP/IP) 堆栈之上。

wifi连接服务器用哪种协议?
TCP:比较原始,需要自己扩展的功能较多,开发效率低
HTTP:比较重,网页端的协议,占用的带宽较大。
MQTT:适用于物联网设备和传感器等低带宽、不稳定网络环境下的通信。

MQTT的特性
-协议心跳包仅两个字节
-协议支持QoS(服务质量)设置,可以确保消息传输的可靠性。
QoS 0:最多一次传输。消息发布者只发送一次消息,不考虑它是否被接收。这种传输方式是最快的,但也是最不可靠的,因为消息可能会丢失。
QoS 1:至少一次传输。消息发布者会发送消息并等待确认,如果确认未收到,则会重发消息。这种传输方式可以保证消息最少被传输一次,但可能会重复传输。
QoS 2:恰好一次传输。消息发布者会发送消息并等待确认,如果确认未收到,则会重发消息。接收者会对消息进行去重和重复检查,以确保消息只被传输一次。这种传输方式是最可靠的,但也是最慢的。
-协议支持发布/订阅模式,可以根据应用场景选择不同的模式。
-可扩展设备影子模式

我们模组固件是支持MQTT的,我们可以使用串口AT配置模组连接云平台

这里用安信可官方提供的串口助手,调试更加便捷:参考飞书文档

AT指令如下:

连接阿里云AT指令介绍
AT+CWMODE=3 设置ESP模块为Station+SoftAP模式;
AT+CWJAP="SSID","PWD" 设置ESP模块连接无线网络 *注意双引号为英文;
AT+MQTTUSERCFG=0,1,"NULL","username","password",0,0,"" MQTT用户属性,username和passwd与下图对应
AT+MQTTCLIENTID=0,"clientId" 设置MQTT 的clientId参数;clientId也与下图对应
AT+MQTTCONN=0,"host",1883,1 连接 MQTT Broker;host与下图mqttHostUrl对应。
AT+MQTTCLEAN 断开MQTT连接。
AT+RST 复位ESP-01S / ESP8266模块;
需要注意AT+MQTTCLIENTID指令中的clientId中的逗号前需要加\符号进行转义,
假设clientId为"xxx|securemode=2,signmethod=hmacsha256,timestamp=xxx|",
那么AT指令应为AT+MQTTCLIENTID=0,"xxx|securemode=2\,signmethod=hmacsha256\,timestamp=xxx|"


下方是我账号的设备信息,可以参考下,替换为自己平台的信息:
AT+CWMODE=3
AT+CWJAP="leo","123456789"
AT+MQTTUSERCFG=0,1,"NULL","device1&kxxxxxxJeE","f3c17488453a7xxxxxxx015f5787b90e68a2",0,0,""
AT+MQTTCLIENTID=0,"k0rqhxxxxeE.device1|securemode=2\,signmethod=hmacsha256\,timestamp=1705652029669|"
AT+MQTTCONN=0,"iot-06z0xxxxxxlwd3.mqtt.iothub.aliyuncs.com",1883,1



更多AT指令可以参考官方文档
https://docs.espressif.com/projects/esp-at/en/release-v2.2.0.0_esp8266/Get_Started/index.html

发送完以上指令后,在阿里云物联网平台中刷新设备网页,你将会看到设备已成功上线的信息。

状态上报-发布

回到平台的产品界面,点击Topic类列表,点击物模型通信,可以看到你自己的状态上报Topic

/sys/xxxx/${deviceName}/thing/event/property/post

然后看下我们这个产品的功能定义中,有哪些需要上报的字段

这里,我们使用当前温度为例,记录标识符CurrentTemperature,当然,我们也可以点击“编辑”添加、删除、修改功能模块。

拿到标识符后,我们就可以通过下方MQTT指令上报状态

C
AT+MQTTPUB=0,"topic字段","data字段",1,0 通过 topic 发布 MQTT消息

特别注意,空格,英文符号
注意,topic中的deviceName改为你创建的设备名称:
AT+MQTTPUB=0,"/sys/k0rqhbzuJeE/d
evice1/thing/event/property/post","{\"params\":{\"CurrentTemperature\":33}}",1,0

然后,我们就可以在设备界面看到上报的温度数据啦:

控制接收-订阅

回到平台的产品界面,点击Topic类列表,点击物模型通信,可以看到你自己的设备设置的Topic

/sys/k0rqhbzuJeE/${deviceName}/thing/service/property/set

然后通过串口调试助手订阅主题

AT+MQTTSUB=0,"topic字段",1  订阅指定 MQTT topic

特别注意,空格,英文符号
AT+MQTTSUB=0,"/sys/k0rqhbzuJeE/device1/thing/service/property/set",1

然后我们可以增加一个开关控制字段

我们随便添加一个支持读写的属性,也可以自己自定义

记得点击发布上线

发布成功后,我们可以模拟手机端操作这个字段,发送控制指令给模组。

修改对应属性,点击设置

然后我们就可以在串口调试助手看到接收的指令

五、STM32驱动WiFi模组

前面我们都是使用串口助手发送指令驱动WiFi模组的,那接下来,我们就可以用STM32替换串口助手驱动WiFi模组啦。

开始这个章节时,可以把下方USB转串口部分拔掉。

这部分的原理图是这样的,因为模组使用串口通信,所以我们只需要关注PC10和PC11的USART3。

创建工程,打开UART4

同时,开启串口中断

配置DMA

调整DMA初始化顺序(重要)

为了更直观看到设备运行状态,打开USART1作为日志口。

生成工程,然后参考串口章节,添加日志打印重定向

main.c
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes *//* USER CODE BEGIN 2 */printf("app init\n");/* USER CODE END 2 *//* USER CODE BEGIN 4 */
int fputc(int ch, FILE *f)
{HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);return ch;
}

添加最简单的AT指令发送和接收判断,通过串口中断+DMA不定长接收


main.c
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
/* USER CODE END Includes *//* USER CODE BEGIN 0 */
#define AT "AT\r\n"
#define ACK_OK "OK"
#define BUFFER_SIZE 256uint8_t rx_buf[BUFFER_SIZE];  //应用处理buffer
bool rx_status = false;
extern DMA_HandleTypeDef hdma_uart4_rx;
uint8_t dma_rxbuf[BUFFER_SIZE];                                       //DMA接收bufferstatic void init(void);
/* USER CODE END 0 *//* USER CODE BEGIN 2 */printf("app init\n");__HAL_UART_ENABLE_IT(&huart4,UART_IT_IDLE);HAL_UART_Receive_DMA(&huart4,dma_rxbuf,sizeof(dma_rxbuf));init();/* USER CODE END 2 *//* USER CODE BEGIN 4 */
void copy_to_rx_buf(uint8_t *ack,uint8_t len){if(rx_status)return;if(len > sizeof(rx_buf))return;memcpy(rx_buf,ack,len);rx_status = true;
}bool send_cmd(uint8_t *cmd,uint8_t *ack){int time_out = 500;memset(rx_buf,0,sizeof(rx_buf));printf("send ----> %s\n",cmd);HAL_UART_Transmit_DMA(&huart4 , (uint8_t *)cmd, strlen((const char *)cmd));while (time_out --){if(rx_status){printf("read ----> %s\n",rx_buf);if(strstr((const char*)rx_buf,(const char*) ack) != NULL){printf("ack ok\n");rx_status = false;return true;}memset(rx_buf,0,sizeof(rx_buf));rx_status = false;}HAL_Delay(10);}printf("ack fail\n");return false;
}void init(){send_cmd((uint8_t *)AT,(uint8_t *)ACK_OK);HAL_Delay(500);
}void UART_IDLEHandler(){if(__HAL_UART_GET_FLAG(&huart4, UART_FLAG_IDLE) == SET) //如果串口处于空闲状态{__HAL_UART_CLEAR_FLAG(&huart4, UART_FLAG_IDLE);//清空空闲状态标志HAL_UART_DMAStop(&huart4); //关闭DMA传输//计算接收到的数据长度 ,已接收长度=需要接收总长度-剩余待接收长度uint8_t rlen = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_uart4_rx);//进行数据处理    copy_to_rx_buf(dma_rxbuf,rlen);//重新打开DMA接收HAL_UART_Receive_DMA(&huart4,dma_rxbuf,sizeof(dma_rxbuf));               }
}int fputc(int ch, FILE *f)
{HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);return ch;
}
/* USER CODE END 4 */mian.h
/* USER CODE BEGIN EFP */
void UART_IDLEHandler(void);
/* USER CODE END EFP */stm32f1xx_it.c
void UART4_IRQHandler(void)
{/* USER CODE BEGIN UART4_IRQn 0 *//* USER CODE END UART4_IRQn 0 */HAL_UART_IRQHandler(&huart4);/* USER CODE BEGIN UART4_IRQn 1 */UART_IDLEHandler();/* USER CODE END UART4_IRQn 1 */
}

烧录运行:

工程参考飞书文档

然后我们可以所有初始化AT指令全部加上,并且发布和订阅对应消息,最终实现温度上报和控制指令接收的功能。

最终版本的main.c如下


/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** Copyright (c) 2024 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#define AT "AT\r\n"
#define AT_WIFI_MODE "AT+CWMODE=3\r\n"
#define AT_SET_PSW "AT+CWJAP=\"Kang\",\"555883200\"\r\n"
#define AT_SET_MQTT_CFG "AT+MQTTUSERCFG=0,1,\"NULL\",\"device1&k0rqhbzuJeE\",\"f3c17488453a737afd24edefdd4ec6862848a0ca2f8866b7015f5787b90e68a2\",0,0,\"\"\r\n"
#define AT_SET_MQTT_CLENTID "AT+MQTTCLIENTID=0,\"k0rqhbzuJeE.device1|securemode=2\\,signmethod=hmacsha256\\,timestamp=1705652029669|\"\r\n"
#define AT_SET_MQTT_CONN "AT+MQTTCONN=0,\"iot-06z00dcx1hvlwd3.mqtt.iothub.aliyuncs.com\",1883,1\r\n"
#define AT_MQTT_SUB "AT+MQTTSUB=0,\"/sys/k0rqhbzuJeE/device1/thing/service/property/set\",1\r\n"
#define AT_MQTT_PUB "AT+MQTTPUB=0,\"/sys/k0rqhbzuJeE/device1/thing/event/property/post\",\"{\\\"params\\\":{\\\"CurrentTemperature\\\":%d}}\",1,0\r\n"
#define ACK_OK "OK"#define BUFFER_SIZE 256uint8_t rx_buf[BUFFER_SIZE];  //应用处理buffer
uint8_t tx_buf[BUFFER_SIZE];  //发送buffer
bool rx_status = false;extern DMA_HandleTypeDef hdma_uart4_rx;
uint8_t dma_rxbuf[BUFFER_SIZE];                                       //DMA接收bufferstatic void init(void);
static bool send_cmd(uint8_t *cmd,uint8_t *ack);
static void read_cmd(void);
/* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_USART1_UART_Init();MX_UART4_Init();/* USER CODE BEGIN 2 */printf("app init\n");__HAL_UART_ENABLE_IT(&huart4,UART_IT_IDLE);HAL_UART_Receive_DMA(&huart4,dma_rxbuf,sizeof(dma_rxbuf));init();printf("wifi init finish\n");uint8_t temperature = 20;uint8_t send_count = 0;/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */send_count ++;if(send_count >= 10){//10s上报一条send_count = 0;sprintf((char *)tx_buf,"AT+MQTTPUB=0,\"/sys/k0rqhbzuJeE/device1/thing/event/property/post\",\"{\\\"params\\\":{\\\"CurrentTemperature\\\":%d}}\",1,0\r\n", temperature);send_cmd(tx_buf,(uint8_t *)ACK_OK);temperature ++;if(temperature > 100)temperature = 20;}read_cmd();HAL_Delay(1000);}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses 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_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 */void copy_to_rx_buf(uint8_t *ack,uint8_t len){if(rx_status)return;if(len > sizeof(rx_buf))return;memcpy(rx_buf,ack,len);rx_status = true;
}bool send_cmd(uint8_t *cmd,uint8_t *ack){int time_out = 500;memset(rx_buf,0,sizeof(rx_buf));printf("send ----> %s\n",cmd);HAL_UART_Transmit_DMA(&huart4 , (uint8_t *)cmd, strlen((const char *)cmd));while (time_out --){if(rx_status){printf("read ----> %s\n",rx_buf);if(strstr((const char*)rx_buf,(const char*) ack) != NULL){printf("ack ok\n");rx_status = false;return true;}memset(rx_buf,0,sizeof(rx_buf));rx_status = false;}HAL_Delay(10);}printf("ack fail\n");return false;
}void read_cmd(){if(rx_status){printf("read ----> %s\n",rx_buf);rx_status = false;}
}void init(){send_cmd((uint8_t *)AT,(uint8_t *)ACK_OK);HAL_Delay(500);send_cmd((uint8_t *)AT_WIFI_MODE,(uint8_t *)ACK_OK);HAL_Delay(500);send_cmd((uint8_t *)AT_SET_PSW,(uint8_t *)ACK_OK);HAL_Delay(500);send_cmd((uint8_t *)AT_SET_MQTT_CFG,(uint8_t *)ACK_OK);HAL_Delay(500);send_cmd((uint8_t *)AT_SET_MQTT_CLENTID,(uint8_t *)ACK_OK);HAL_Delay(500);send_cmd((uint8_t *)AT_SET_MQTT_CONN,(uint8_t *)ACK_OK);HAL_Delay(500);send_cmd((uint8_t *)AT_MQTT_SUB,(uint8_t *)ACK_OK);HAL_Delay(500);
}void UART_IDLEHandler(){if(__HAL_UART_GET_FLAG(&huart4, UART_FLAG_IDLE) == SET) //如果串口处于空闲状态{__HAL_UART_CLEAR_FLAG(&huart4, UART_FLAG_IDLE);//清空空闲状态标志HAL_UART_DMAStop(&huart4); //关闭DMA传输//计算接收到的数据长度 ,已接收长度=需要接收总长度-剩余待接收长度uint8_t rlen = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_uart4_rx);//进行数据处理    copy_to_rx_buf(dma_rxbuf,rlen);//重新打开DMA接收HAL_UART_Receive_DMA(&huart4,dma_rxbuf,sizeof(dma_rxbuf));               }
}int fputc(int ch, FILE *f)
{HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);return ch;
}/* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();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,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

设备连接平台成功后,每间隔10s上报一次温度数据

在云平台设置灯开关,设备也会收到消息

工程参考:飞书文档

上面的工程仅仅使用数据复制的方式进行拷贝处理,当交互频繁时,可能会出现丢失指令的情况,下方的工程使用环形缓冲区,可以避免指令丢失情况。

参考飞书文档

手机APP开发参考

采用MQTT协议实现Android APP与阿里云平台的连接_android连接mqtt 阿里云

相关文章:

单片机-STM32部分:18、WiFi模组

飞书文档https://x509p6c8to.feishu.cn/wiki/WFmqwImDViDUezkF7ercZuNDnve 一、WiFi模组应用 当设备需要连接网络&#xff0c;实现远程控制&#xff0c;状态监控时&#xff0c;就需要添加通信模组&#xff0c;常见的通信模组WiFi模组、2G模组、4G模组等&#xff1a; 我们的板卡…...

TLS 1.3黑魔法:从协议破解到极致性能调优

一、TLS协议逆向工程实验 1.1 密码学套件破解剧场 实验准备&#xff1a; 靶机&#xff1a;启用TLS 1.2的Nginx服务器 工具集&#xff1a;Wireshark OpenSSL s_client 定制Python脚本 实战攻击复现&#xff1a; # 强制使用弱加密套件连接 openssl s_client -connect exa…...

职业院校物联网安装调试员(工业数智技术)实训解决方案

一、物联网安装调试员 &#xff08;1&#xff09;职业定义&#xff1a; 利用检测仪器和专用工具&#xff0c;安装、配置、调试物联网产品与设备的人员。其工作任务就是要搭建数据互联的信息网络&#xff0c;并通过电子标签将真实的物体上网连接&#xff0c;并通过对各类设备的…...

mongodb用systemctl启动code=killed, signal=ABRT

参照在 Ubuntu 上安装 MongoDB Community Edition - 数据库手册 v8.0 - MongoDB Docs 安装后&#xff0c;sudo systemctl start mongod启动失败。 sudo systemctl status mongod 结果&#xff1a; mongod.service - MongoDB Database ServerLoaded: loaded (/lib/systemd/sys…...

基于51单片机和8X8点阵屏、矩阵按键的匹对消除类小游戏

目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码1、8X8点阵屏2、矩阵按键3、定时器04、定时器1 四、主函数总结 系列文章目录 前言 用的是普中A2开发板&#xff0c;用到板上的8X8LED点阵屏和矩阵按键。 【单片机】STC89C52RC 【频率】12T11.0592MHz 效果查看/操…...

智能呼叫系统中的NLP意图理解:核心技术解析与实战

引言&#xff1a;当AI拿起电话时 在智能客服、电话营销等场景中&#xff0c;智能呼叫系统正以每年23%的增长率重塑人机交互方式。而支撑这一变革的核心技术&#xff0c;正是自然语言处理&#xff08;NLP&#xff09;中的意图理解模块。本文将深入解析意图理解的技术原理&#…...

信号灯和旋钮在接地电阻柜内的作用主要包括以下几个方面

信号灯的作用‌&#xff1a; ‌指示状态‌&#xff1a;信号灯用于指示接地电阻柜的工作状态&#xff0c;如正常运行、故障报警等。通过不同颜色的灯光&#xff08;如红色表示故障&#xff0c;绿色表示正常&#xff09;来提醒操作人员柜子的当前状态&#xff0c;确保及时处理潜…...

MongoDB 应用实战

1. java 原生客户端 引入maven 1 <dependencies> 2 <dependency> 3 <groupId>org.mongodb</groupId> 4 <artifactId>mongodb‐driver‐sync</artifactId> 5 <version>4.1.1</version> 6 </dependency> 7 </depende…...

Java EE初阶——wait 和 notify

1. 线程饥饿 线程饥饿是指一个或多个线程因长期无法获取所需资源&#xff08;如锁&#xff0c;CPU时间等&#xff09;而持续处于等待状态&#xff0c;导致其任务无法推进的现象。 典型场景 优先级抢占&#xff1a; 在支持线程优先级的系统中&#xff0c;高优先级线程可能持续…...

SpringBoot--Bean管理详解

Bean管理 Bean扫描 回顾spring&#xff1a; 在XML配置文件中&#xff0c;可以借助 <context:component-scan base-package "com.lyc"> 或者注解 ComponentScan(basePackages"com.lyc") 再springboot项目中&#xff0c;既没有标签&#xff0c;也…...

python爬虫实战训练

前言&#xff1a;哇&#xff0c;今天终于能访问豆瓣了&#xff0c;前几天爬太多次了&#xff0c;网页都不让我访问了&#xff08;要登录&#xff09;。 先来个小练习试试手吧&#xff01; 爬取豆瓣第一页&#xff08;多页同上篇文章&#xff09;所有电影的排名、电影名称、星…...

探索大型语言模型(LLM)的开源学习路径:mlabonne/llm-course 深度解析

引言:为什么LLM学习需要系统化课程? 近年来,大型语言模型(Large Language Models, LLMs)彻底改变了自然语言处理领域。从GPT系列到Llama、Mistral等开源模型,掌握LLM的开发和应用能力已成为技术人员的核心竞争力。然而,LLM技术栈涵盖从理论基础到工程实践的复杂内容,如…...

IDEA怎么汉化idea中文改回英文版

第一步:点击左上角的File&#xff0c;然后选择Setting 第二步&#xff1a;Setting页面选择 Appearance & Behavior&#xff0c;然后展开System Settings&#xff0c;然后选择 Language and Region&#xff0c;进行修改 我操作的是2024年的版本 File->Settings -> Ap…...

Flutter目录结构介绍、入口、Widget、Center组件、Text组件、MaterialApp组件、Scaffold组件

目录 1. 创建Flutter项目 1.1使用Android Studio创建Flutter项目 1.2 使用命令行创建Flutter项目 2. Flutter项目介绍 2.1所有代码都在lib目录下编写 2.1 pubspec.yaml 依赖库/图片的引用 ​编辑 3. 运行项目 4. 编写mian.dart文件 4.1 使用MaterialApp 和 Scaffold两个组件…...

C++23 中的 ranges::fold_left:范围折叠算法

文章目录 1. **ranges::fold_left 的基本概念**2. **使用示例**示例 1&#xff1a;计算整数范围的和示例 2&#xff1a;计算字符串范围的连接示例 3&#xff1a;使用自定义函数 3. **与其他折叠算法的比较**4. **为什么需要 ranges::fold_left**5. **总结** 随着 C23 的到来&am…...

Vue2项目created不执行

Vue2项目created不执行 设置唯一值 name在 created 调用方法在 watch 中监听路由完整代码示例 设置唯一值 name 在 Vue 组件中&#xff0c;name 属性用于标识组件。确保每个组件的 name 属性是唯一的&#xff0c;这有助于在调试和开发过程中更好地识别组件。 export default …...

mysql的not exists走索引吗

在MySQL中&#xff0c;​NOT EXISTS子句是否使用索引取决于子查询中关联字段是否建立了合适的索引。以下是关键点总结&#xff1a; ​索引的作用​&#xff1a; 当子查询的关联字段&#xff08;例如B.a_id&#xff09;存在索引&#xff08;如普通B-tree索引&#xff09;时&…...

红黑树实现

1.红黑树的概念 红黑树是一棵二叉搜索树&#xff0c;他的每个节点增加一个存储位来表示节点的颜色&#xff0c;可以是红丝或者黑色。通过对任何一条从根到叶子的路径上各个节点的颜色进行约束&#xff0c;红黑树确保没有一条路径会比其他路径长出两倍&#xff0c;因而是接近平…...

将已打包好的aar文件,上传到 Coding 的 Maven 仓库

将已打包好的aar文件&#xff0c;上传到 Coding 的 Maven 仓库。 在android stuio项目的build.gradle 进行上传。 编写代码 plugins {id maven-publish }// 配置要上传的本地 AAR 文件 def aarFile file(D:\\mylibrary-1.0.0.aar)publishing {publications {mavenAar(MavenP…...

海康相机连接测试-极简版

文章目录 1、下载客户端 1、下载客户端 海康机器人官网下载软件 软件下载地址 先下载客户端测试连接 按照你的相机的类型选择客户端 安装完毕后&#xff0c;确保USB线插的是3.0的端口 软件会自动识别相机型号 在上方有播放按钮&#xff0c;可以采集图像信息显示...

深入探索:Core Web Vitals 进阶优化与新兴指标

一、INP&#xff08;Interaction to Next Paint&#xff09;深度解析 INP 与 FID 的核心差异 • 响应范围&#xff1a;FID仅测量首次输入延迟&#xff0c;而INP跟踪页面生命周期中所有关键交互 • 测量维度&#xff1a;INP综合考虑输入延迟、处理时间和下一帧渲染时间 • 评…...

AI与产品架构设计系列(2):Agent系统的应用架构与落地实

什么是AI Agent&#xff1f;其在架构中的独特定位 AI Agent&#xff08;人工智能代理&#xff09;是一种模拟人类智能行为的自主系统&#xff0c;通常以大型语言模型&#xff08;LLM&#xff09;作为核心引擎。简单来说&#xff0c;Agent能够像人一样感知环境信息、规划行动方…...

OpenAI与微软洽谈新融资及IPO,Instagram因TikTok流失四成用户

OpenAI与微软洽谈新融资及IPO 据悉&#xff0c;OpenAI 正与微软洽谈新融资及筹备 IPO&#xff0c;关键问题是微软在 OpenAI 重组后的股权比例。微软已投资超 130 亿美元&#xff0c;双方修订 2019 年合同&#xff0c;微软拟弃部分股权换新技术访问权。OpenAI 上周放弃了有争议转…...

架构篇、第五章_05Jenkins的部署与构建

Linux_架构篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;Jenkins的部署与构建 版本号: 1.0,0 作者: 老王要学习 日期: 2025.05.15 适用环境: Centos7 文档说明 本文档围绕 Jenkins 的部署与构建展开&a…...

`ParameterizedType` 和 `TypeVariable` 的区别

在 Java 的泛型系统中&#xff0c;ParameterizedType 和 TypeVariable 是两个不同的类型表示&#xff0c;它们都属于 java.lang.reflect.Type 接口的子接口。两者都在反射&#xff08;Reflection&#xff09;中用于描述泛型信息&#xff0c;但用途和含义不同。 &#x1f31f; 一…...

HTML 中的 input 标签详解

HTML 中的 input 标签详解 一、基础概念 1. 定义与作用 HTML 中的 <input> 标签是表单元素的核心组件&#xff0c;用于创建各种用户输入字段。作为一个空标签&#xff08;没有闭合标签&#xff09;&#xff0c;它通过 type 属性来决定呈现何种输入控件&#xff0c;是实…...

从 Vue3 回望 Vue2:性能优化内建化——从黑盒优化到可控编译

文章目录 从 Vue3 回望 Vue2&#xff1a;性能优化内建化——从黑盒优化到可控编译1. 引言2. Vue2 的性能优化机制解析3. Vue3 的编译期优化能力拆解3.1 静态提升&#xff08;Static Hoisting&#xff09;3.2 Patch Flag 精确标记3.3 Block Tree &#xff08;块级更新边界&#…...

HOW - React NextJS 的同构机制

文章目录 一、什么是 Next.js 的同构&#xff1f;二、核心目录结构三、关键函数&#xff1a;如何实现不同渲染方式&#xff1f;1. getServerSideProps —— 实现 SSR&#xff08;每次请求动态获取数据&#xff09;2. getStaticProps getStaticPaths —— 实现 SSG&#xff08;…...

电动汽车直流快充充电桩AEV200-DC240M4的详细介绍

电动汽车直流快充充电桩AEV200-DC240M4产品简介 AEV系列为全新一代分体式电动汽车直流恒功率快速充电机。系统内置 30/40kW 恒功率充电模块&#xff0c;最高输出电压1000V&#xff0c;满足各类车辆充电需求。模块采用隔离风道灌胶设 计 &#xff0c;可靠性高 &#xff0c;可应…...

YOLOv7训练时4个类别只出2个类别

正常是4个类别&#xff1a; 但是YOLOv7训练完后预测总是只有两个类别&#xff1a; 而且都是LFM和SFM 我一开始检查了下特征图大小&#xff0c;如果输入是640*640的话&#xff0c;三个尺度特征图是80*80,40*40,20*20&#xff1b;如果输入是416*416的话&#xff0c;三个尺度特征…...

数据赋能(224)——数据与业务协同——数据动态调整原则

概述 数据动态调整原则不仅能帮助组织迅速响应业务需求和技术环境的变化&#xff0c;还能确保数据应用始终与最新的数据处理技术、算法和工具保持同步。通过实施数据动态调整&#xff0c;企业能够更准确地捕捉业务趋势&#xff0c;优化数据质量&#xff0c;以及提高资源利用效…...

Vulfocus靶场-文件上传-3

WSO2 文件上传 &#xff08;CVE-2022-29464&#xff09; WSO2是一家成立于 2005 年的开源技术提供商。它提供了一个企业平台&#xff0c;用于在本地和整个 Internet 上 集成应用程序编程接口(API)、应用程序和 Web 服务。 某些 WSO2 产品允许无限制的文件上传和远程代码执行。…...

(for 循环) VS (LINQ) 性能比拼 ——c#

在大多数情况下&#xff0c;for 循环的原始性能会优于 LINQ&#xff0c;尤其是在处理简单遍历、数据筛选或属性提取等场景时。这是由两者的实现机制和抽象层次决定的。以下是具体分析&#xff1a; 一、for 循环与 LINQ 的性能差异原因 1. 抽象层次与执行机制 for 循环&#…...

自学嵌入式 day19-数据结构 链表

二、线性表的链式存储 1.特点&#xff1a; &#xff08;1&#xff09;线性表链式存储结构的特点是一组任意的存储单位存储线性表的数据元素&#xff0c;存储单元可以是连续的&#xff0c;也可以不连续。可以被存储在任意内存未被占用的位置上。 &#xff08;2&#xff09;所以…...

一发入魂:极简解决 SwiftUI 复杂视图未能正确刷新的问题(中)

概述 各位似秃非秃小码农们都知道,在 SwiftUI 中视图是状态的函数,这意味着状态的改变会导致界面被刷新。 但是,对于有些复杂布局的 SwiftUI 视图来说,它们的界面并不能直接映射到对应的状态上去。这就会造成一个问题:状态的改变并没有及时的引起 UI 的变化。 如上图所示…...

UI自动化测试详解

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、about自动化测试 定义&#xff1a;把人为驱动的测试转化为机器执行的一种过程&#xff0c;重点在于持续集成这个概念&#xff1b; 优势&#xff1a;节约人力…...

数学复习笔记 14

前言 和家里人交流了一下&#xff0c;他们还是希望我全力以赴初试&#xff0c;我确实也得放开了干&#xff0c;不要束手束脚的。好好加油。感觉公共课都没有啥压力&#xff0c;主要是专业课要好好加油&#xff0c;真不能过不了线&#xff0c;要是过不了线&#xff0c;啥都白搭…...

单元化架构

目录 ​​​​​​​​编辑 单元化 逻辑单元 单元化 多地多机房部署&#xff0c;是互联网系统的必然发展方向&#xff0c;一个系统要走到这一步&#xff0c;也就必然要解决上面提到的问题&#xff1a;流量调配、数据拆分、延时等。业界有很多技术方案可以用来解决这些问题&…...

硬件厂商的MIB文档详解 | 如何查询OID? | MIB Browser实战指南-优雅草卓伊凡

硬件厂商的MIB文档详解 | 如何查询OID? | MIB Browser实战指南-优雅草卓伊凡 一、硬件厂商的MIB文档是什么&#xff1f; 1. MIB的本质&#xff1a;设备的”数据字典” MIB&#xff08;Management Information Base&#xff09; 是SNMP协议的核心数据库&#xff0c;定义了设备…...

遥感图像露天矿区检测数据集VOC+YOLO格式1542张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1542 标注数量(xml文件个数)&#xff1a;1542 标注数量(txt文件个数)&#xff1a;1542 …...

【python基础知识】Day 27 函数专题2:装饰器

知识点&#xff1a; 装饰器的思想&#xff1a;进一步复用函数的装饰器写法注意内部函数的返回值 装饰器教程 作业&#xff1a; 编写一个装饰器 logger&#xff0c;在函数执行前后打印日志信息&#xff08;如函数名、参数、返回值&#xff09; def logger(func):def wrapper(*ar…...

游戏站的几种形式

游戏站点的主要形式&#xff1a;单品游戏站、游戏盒子站与单类型游戏盒子站 随着互联网的普及和游戏产业的快速发展&#xff0c;游戏站点作为玩家获取游戏资源和信息的重要平台&#xff0c;呈现出多种形式。本文将分析三种常见的游戏站点形式&#xff1a;单品游戏站、游戏盒子站…...

动态IP赋能业务增效:技术解构与实战应用指南

在数字化转型加速的今天&#xff0c;IP地址作为网络通信的基础设施&#xff0c;其技术特性正深刻影响着企业业务架构的效率与安全性。动态IP&#xff08;Dynamic IP&#xff09;作为互联网资源分配的核心机制&#xff0c;早已突破传统认知中的"临时地址"定位&#xf…...

Redis 五种类型基础操作(redis-cli + Spring Data Redis)

目录 一、什么是 Redis&#xff1f; 二、Redis 的特点 三、Redis 常见的数据类型 四、Redis 的典型应用场景 五、redis-cli&#xff08;命令行工具&#xff09;练习命令 1.1、String 类型&#xff08;最基本的数据类型&#xff09; 1.2、List 类型&#xff08;链表结构&a…...

Gitee DevOps:中国企业数字化转型的加速引擎

随着中国数字经济规模突破50万亿元大关&#xff0c;研发效能已成为企业数字化转型的核心竞争力指标。在2025年这个关键节点&#xff0c;中国企业面临的不再是是否采用DevOps的选择题&#xff0c;而是如何选择最适合本土环境的DevOps平台的战略决策。Gitee DevOps平台凭借其独特…...

【数据仓库面试题合集①】数据建模高频面试题及解析

🧠 面试官爱问什么?——核心考察点 数据建模作为数仓岗位面试的重头戏,考察的不只是模型知识,更是对业务理解、抽象能力和工程落地经验的综合评估。常见题型可分为三类: 概念类:模型类型、建模方法论(如维度建模、范式建模) 场景类:给定一个业务场景进行模型设计(如…...

华为云Flexus+DeepSeek征文|SpringBoot开发实战:基于ModelArts Studio高效集成DeepSeek大模型服务

目录 一、前言 二、ModelArts Studio&#xff08;MaaS&#xff09;介绍与使用 2.1ModelArts Studio&#xff08;MaaS&#xff09;介绍 2.2 ModelArts Studio&#xff08;MaaS&#xff09;使用场景 2.3 开通MaaS服务 2.4 开通DeepSeek-V3商用服务 三、MaaS模型服务接口测试 3.1 …...

【C++】类与对象

C语言结构体中只能定义变量,在C中,结构体内不仅可以定义变量,也可以定义函数。比如:之前在数据结构中,用C语言方式实现的栈,结构体中只能定义变量;现在以C方式实现,会发现struct中也可以定义函数。 struct Stack {// 成员函数void Init(int defaultCapacity 4){a (int*)mall…...

mac M芯片运行docker-desktop异常问题

虽然mac已经迭代到m4了&#xff0c;但官方的docker-desktop运行仍然有问题&#xff0c;包括但不限于&#xff1a; 命令行docker找不到docker-desk打不开docker-desktop闪退容器起不来 尝试不同版本后&#xff0c;看到了其他可以在mac跑docker的开源方法&#xff0c;更简单、轻…...

5G 技术在智能制造中的应用:加速工业革命的新引擎

5G 技术在智能制造中的应用:加速工业革命的新引擎 在过去几十年里,制造业经历了从机械化到自动化,再到如今的智能化变革。而 5G 技术的出现,不仅是一次通信技术的升级,更是为 智能制造 注入了新的动力。从 智能工厂、工业物联网(IIoT) 到 远程控制与数据智能分析,5G 正…...