单片机-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指令上报状态

AT+MQTTPUB=0,"topic字段","data字段",1,0 通过 topic 发布 MQTT消息特别注意,空格,英文符号
注意,topic中的deviceName改为你创建的设备名称:
AT+MQTTPUB=0,"/sys/k0rqhbzuJeE/device1/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 阿里云

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/81033.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

探索Qwen2ForCausalLM 架构上进行微调

简述 试验参考了mini_qwen 的开源实现 GitHub - qiufengqijun/mini_qwen: 这是一个从头训练大语言模型的项目&#xff0c;包括预训练、微调和直接偏好优化&#xff0c;模型拥有1B参数&#xff0c;支持中英文。这是一个从头训练大语言模型的项目&#xff0c;包括预训练、微调和…

hysAnalyser特色的TS流编辑、剪辑和转存MP4功能说明

摘要 hysAnalyser 是一款特色的 MPEG-TS 数据分析工具&#xff0c;融合了常规TS文件的剪辑&#xff0c;转存功能&#xff0c;可用于平常的视频开发和测试。 本文详细阐述了对MPEG-TS 流的节目ID&#xff0c;名称&#xff0c;PID&#xff0c;时间戳&#xff0c;流类型&#xff…

前端[插件化]设计思想_Vue、React、Webpack、Vite、Element Plus、Ant Design

前端插件化设计思想旨在提升应用的可扩展性、可维护性和模块化程度。这种思想不仅体现在框架&#xff08;如 Vue、React&#xff09;中&#xff0c;也广泛应用于构建工具&#xff08;如 Webpack、Vite&#xff09;以及 UI 库&#xff08;如 Element Plus、Ant Design&#xff0…

2025年高防IP与游戏盾深度对比:如何选择最佳防护方案?

2025年&#xff0c;随着DDoS攻击规模的指数级增长和混合攻击的常态化&#xff0c;高防IP与游戏盾成为企业网络安全的核心选择。然而&#xff0c;两者在功能定位、技术实现及适用场景上存在显著差异。本文结合最新行业实践与技术趋势&#xff0c;全面解析两者的优劣&#xff0c;…

日志根因分析:Elastic Observability 的异常检测与日志分类功能

作者&#xff1a;来自 Elastic Bahubali Shetti Elastic Observability 不仅提供日志聚合、指标分析、APM 和分布式追踪&#xff0c;Elastic 的机器学习能力还能帮助分析问题的根因&#xff0c;让你将时间专注于最重要的任务。 随着越来越多的应用程序迁移到云端&#xff0c;收…

Linux火墙管理及优化

网络环境配置 使用3个新的虚拟机【配置好软件仓库和网络的】 F1 192.168.150.133 NAT F2 192.168.150.134 192.168.10.20 NAT HOST-ONLY 网络适配仅主机 F3 192.168.10.30 HOST-ONLY 网络适配仅主机 1 ~]# hostnamectl hostname double1.timinglee.org 【更…

java配置webSocket、前端使用uniapp连接

一、这个管理系统是基于若依框架&#xff0c;配置webSocKet的maven依赖 <!--websocket--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency> 二、配…

基于Yolov8+PyQT的老人摔倒识别系统源码

概述 ​​基于Yolov8PyQT的老人摔倒识别系统​​&#xff0c;该系统通过深度学习算法实时检测人体姿态&#xff0c;精准识别站立、摔倒中等3种状态&#xff0c;为家庭或养老机构提供及时预警功能。 主要内容 ​​完整可运行代码​​ 项目采用Yolov8目标检测框架结合PyQT5开发…

Oracle 创建外部表

找别人要一下数据&#xff0c;但是他发来一个 xxx.csv 文件&#xff0c;怎么办&#xff1f; 1、使用视图化工具导入 使用导入工具导入&#xff0c;如 DBeaver&#xff0c;右击要导入的表&#xff0c;选择导入数据。 选择对应的 csv 文件&#xff0c;下一步就行了&#xff08;如…

【华为OD- B卷 01 - 传递悄悄话 100分(python、java、c、c++、js)】

【华为OD- B卷 01 - 传递悄悄话 100分(python、java、c、c++、js)】 题目 给定一个二叉树,每个节点上站一个人,节点数字表示父节点到该节点传递悄悄话需要花费的时间。 初始时,根节点所在位置的人有一个悄悄话想要传递给其他人,求二叉树所有节点上的人都接收到悄悄话花…

房贷利率计算前端小程序

利率计算前端小程序 视图效果展示如下&#xff1a; 在这里插入代码片 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&qu…

自制操作系统day8 (鼠标数据取得、通往32位模式之路、A20GATE、切换到保护模式、控制寄存器cr0-cr4以及cr8、ALIGNB)

day8 鼠标数据取得方法 fifo8_init(&mousefifo, 128, mousebuf); for (;;) { io_cli(); if (fifo8_status(&keyfifo) fifo8_status(&mousefifo) 0) { io_stihlt(); } else { if (fifo8_status(&keyfifo) ! 0) { i fifo8_get(&keyfifo); io_sti(); spr…

IP大科普:住宅IP、机房IP、原生IP、双ISP

不同类型的IP在跨境电商、广告营销、网络技术、数据收集等领域都有广泛应用&#xff0c;比如常见的住宅IP、机房IP、原生IP、双ISP等&#xff0c;这些IP分别都有什么特点&#xff0c;发挥什么作用&#xff0c;适合哪些业务场景&#xff1f; 一、IP类型及其作用 1.住宅IP 住宅…

Elasticsearch面试题带答案

Elasticsearch面试题带答案 Elasticsearch面试题及答案【最新版】Elasticsearch高级面试题大全(2025版),发现网上很多Elasticsearch面试题及答案整理都没有答案,所以花了很长时间搜集,本套Elasticsearch面试题大全,Elasticsearch面试题大汇总,有大量经典的Elasticsearch面…

Eigen与OpenCV矩阵操作全面对比:最大值、最小值、平均值

功能对比总表 功能Eigen 方法OpenCV 方法主要区别最大值mat.maxCoeff(&row, &col)cv::minMaxLoc(mat, NULL, &maxVal, NULL, &maxLoc)Eigen需要分开调用&#xff0c;OpenCV一次获取最小值mat.minCoeff(&row, &col)cv::minMaxLoc(mat, &minVal, NU…

echarts之双折线渐变图

vue3echarts实现双折线渐变图 echarts中文官网&#xff1a;https://echarts.apache.org/examples/zh/index.html 效果图展示&#xff1a; 整体代码如下&#xff1a; <template><div id"lineChart" style"width:100%;height:400px;"></di…

MD编辑器推荐【Obsidian】含下载安装和实用教程

为什么推荐 Obsidian &#xff1f; 免费 &#xff08;Typora 开始收费了&#xff09;Typora 实现的功能&#xff0c;它都有&#xff01;代码块可一键复制 文件目录支持文件夹 大纲支持折叠、搜索 特色功能 – 白板 特色功能 – 关系图谱 下载 https://pan.baidu.com/s/1I1fSly…

vue 鼠标经过时显示/隐藏其他元素

方式一&#xff1a; 使用纯css方式 , :hover是可以控制其他元素 1、 当两个元素是父子关系 <div class"all_" ><div> <i class"iconfont icon-sun sun"></i></div> </div> .all_{} .sun {display: none; /* 默认…

静态网站部署:如何通过GitHub免费部署一个静态网站

GitHub提供的免费静态网站托管服务可以无需担心昂贵的服务器费用和复杂的设置步骤&#xff0c;本篇文章中将一步步解如何通过GitHub免费部署一个静态网站&#xff0c;帮助大家将创意和作品快速展现给世界。 目录 了解基础情况 创建基础站点 在线调试站点 前端项目部署 部署…

Pytorch里面多任务Loss是加起来还是分别backward? | Pytorch | 深度学习

当你在深度学习中进入“多任务学习(Multi-task Learning)”的领域,第一道关卡可能不是设计网络结构,也不是准备数据集,而是:多个Loss到底是加起来一起backward,还是分别backward? 这个问题看似简单,却涉及PyTorch计算图的构建逻辑、自动求导机制、内存管理、任务耦合…