STM32连接阿里云物联网平台
文章目录
- 引言
- 一、STM32连接阿里云物联网平台思路
- 二、ESP8266烧录固件
- 三、使用AT指令连接阿里云物联网平台
- 四、STM32环形串口缓冲区驱动程序
- 五、STM32连接阿里云驱动程序
引言
连续写了两篇关于阿里云连接的文章,都是使用Arduino ESP8266 & Arduino ESP32的方式:
1、Arduino ESP8266&ESP32使用AliyunIoTSDK.h连接阿里云物联网平台
2、使用ArduinoMqttClient库连接阿里云,并实现发送接收数据(ESP8266)
但是,使用Arduino的方式处理阿里云的数据在简单的电子小制作、毕业设计完全可以胜任,但是Arduino的核心库都是对底层进行二次封装,运行效率难免会低一些。ESP8266与ESP32作为主控的MCU,功能有限,在这种情况下,就会想到使用STM32搭载一个可以联网的芯片来做数据物联网的开发了。
我在企业开发项目中,接触过新能源汽车充电桩,核心是使用STM32F103VET6进行主控,它通过搭载一个4G模块来对云端数据进行处理。设备上线时,后台有上线的提示;管理员可用户都可以查看到充电桩的实时状态,如下图所示,管理员坐在办公室便可以查看到有两个慢充桩处于空闲状态,有一个充电桩已连接车辆,但是并没有开始充电,我们就可以猜测出用户可能刚刚到达,已将插枪连接车辆,正在进行扫码的操作。
平台更会报一些简单的故障,如急停故障,充电桩安装的环境多种多样,有时会有小朋友误触到急停故障导致充电桩报故障,不能充电,这时用户打电话给运维的客服,便可以在运维客服的提示下把急停按键复原,充电桩便可以正常使用。
通过物联网技术来对新能源汽车充电桩进行控制大大的减轻的运营的成本,在其他行业如加油站无人值守的自动洗车机、学校教学楼的自动贩卖机等等,都是物联网控制的一个范例,如今物联网控制技术更是影响着我们生活的方方面面。
文章使用基础型STM32F1作为主控芯片,搭载ESP8266模块,使用环形串口缓冲区算法连接阿里云平台,实现数据的订阅和发布。文章有一点点的深度,我尽量把每一个细节每一个步都骤掰开来,嚼碎去详细说明。
有很多例程在数据收发这一块都使用数组进行数据的接收,对付一些简单的物联网开发完全是没有问题的,而环形串口缓冲区算法是在大规模数据接收时使用的,在这么简单的物联网数据收发中使用难免会影响效率。
是的,在这么简单的物联网数据的收发我还使用环形串口缓冲区,影响一定的运行的效率是一定。但是,长期做开发的一定要留有有方便后期升级维护的一个思维,一些升级接口需要多预留一些,产品都不是一次做出来的,是一代一代逐渐完善,逐渐更新换代的。
一、STM32连接阿里云物联网平台思路
连接思路其实很简单,只要两步就好了:
1、烧录MQTT的固件
2、STM32使能串口通过AT指令连接阿里云物联网平台
我使用的是ESP8266作为联网设备,连接阿里云物联网平台,之前我使用Arduino的方式连接阿里云平台也是同样的原理,但是Arduino将一些底层的函数进行了封装,我们只需要学会使用Arduino提供的一些库便可以连接阿里云物联网平台进行数据交换。
而STM32则没有相关的库,我们需要烧录好MQTT的固件,通过电脑串口助手先连接成功阿里云物联网平台,测试好订阅和发布的功能后,连接我们STM32串口,进行简单的串口数据收发。
二、ESP8266烧录固件
连接物联网云平台之前先需要烧录相关固件,固件地址如下:
安信可固件汇总
我选择第4个固件,固件的下载地址附有官方的使用说明。
因为技术更新迭代的速度非常快,可能我的这一篇文章发布几个月或半年后固件便开始更新,但都是换汤不换药,掌握了连接的原理,我们也可以自行查看官方的文档来进行连接,此篇文章可以作为非常重要的参考使用。
下载完成后便可以烧录固件,烧录固件的软件可以通过官网下载,我这里也提供一个网盘下载连接:
链接:https://pan.baidu.com/s/1H96ZbeG_ogslrJUz2Y162w
提取码:wslw
软件打开后,安装提示很容易就可以将固件烧录进去,下图是我烧录成功的截图界面:
三、使用AT指令连接阿里云物联网平台
固件烧录成功之后,首先需要使用电脑的串口助手软件,通过简单的AT指令连接WiFi,连接阿里云,然后完成订阅和发布数据,确认好AT指令代码没有问题后,我们才把相关的AT指令封装起来,写进程序中,连接的AT指令只需要熟悉常用的几个即可。
指令 | 功能 |
---|---|
AT | 测试 |
AT+RST | 重启 |
AT+CWMODE=1 | 设置为无线终端模式,可以连接WiFi |
AT+CWJAP=“wifi”,“passwd” | 连接热点 |
AT+CWSTATE? | 查询WiFi连接讲解状态 |
AT+MQTTUSERCFG=0,1,“clientId”,“username”,“password”,0,0,“” | 设置MQTT连接参数 |
AT+MQTTCONN=0,“Broker Address”,1883,1 | 连接阿里云 |
AT+MQTTCONN? | 查询MQTT连接状态 |
AT+MQTTSUB=0,“topic”,0 | 订阅主题 |
AT+MQTTPUB=0,“topic”,“msg” | 发布主题 |
AT指令按照如下通过串口助手发送到ESP8266即可完成阿里云的连接
(1)判断ESP8266是否存在
使用指令:AT\r\n
串口回复:OK
备注:使用串口助手和STM32发送指令的时候,结尾加上\r\n,表示指令结束
(2)设置ESP8266为接入点模式
使用指令:AT+CWMODE=1\r\n
串口回复:OK
(3)查询WiFi是否连接成功
使用指令:AT+CWSTATE?
串口回复:
+CWSTATE:2"CMCC-H3qz" //备注:CMCC-H3qz是我的WiFi名OK
(4)如果没有连接WiF,则连接WiFi
使用指令:AT+CWJAP=“CMCC-H3qz”,“7xf47uxf”\r\n
备注:CMCC-H3qz是我的WiFi名,7xf47uxf是我的WiFi密码
串口回复:
AT+CWJAP="CMCC-H3qz""7xf47uxf"
WIFI DISCONNECT
WIFI CONNECTED
WIFI GOT IPOK
(5)设置MQTT参数
使用指令:
AT+MQTTUSERCFG=0,1,“clientId”,“username”,“password”,0,0,“”\r\n
示例:
AT+MQTTUSERCFG=0,1,"k0efkfcSwlt.Device_Demo|securemode=2\,signmethod=hmacsha256\,timestamp=1705149361131|","Device_Demo&k0efkfcSwlt","ea343ac1d6a4eec7b057bcab39a2ffdf88abc8b810ec0dc7ca3de91a56292ff9",0,0,""\r\n
备注:
1、指令中,遇到“,”字符时,使用“\”隔开,避免报错。
2、clientId,username,password这些数据从阿里云物联网平台获取
串口回复:
AT+MQTTUSERCFG=01"k0efkfcSwlt.Device_Demo|securemode=2\signmethod=hmacsha256\timestamp=1705149361131|""Device_Demo&k0efkfcSwlt""ea343ac1d6a4eec7b057bcab39a2ffdf88abc8b810ec0dc7ca3de91a56292ff9"00""OK
(6)连接阿里云
使用指令:AT+MQTTCONN=0,“Broker Address”,1883,1\r\n
示例:
AT+MQTTCONN=0,"k0efkfcSwlt.iot-as-mqtt.cn-shanghai.aliyuncs.com",1883,1\r\n
备注:
Broker Address为MQTT域名,阿里云物联网平台开发者文档有详细的解释,我在以前的文章也对这方面进行了详细的说明,大家可以尝试一下使用MQTT.fx连接阿里云平台就能明白MQTT域名的作用了。
附上这部分云文档的地址:
使用MQTT.fx接入物联网平台
串口回复:
AT+MQTTCONN=0"k0efkfcSwlt.iot-as-mqtt.cn-shanghai.aliyuncs.com"18831
+MQTTCONNECTED:01"k0efkfcSwlt.iot-as-mqtt.cn-shanghai.aliyuncs.com""1883"""1OK
这时,我们登陆阿里云物联网平台,可以看到设备已成功连接阿里云物联网平台。
(7)发布数据
使用指令:AT+MQTTPUB=0,“topic”,“msg”\r\n
示例:
AT+MQTTPUB=0,"/sys/k0efkfcSwlt/Device_Demo/thing/event/property/post","{\"params\": {\"temperature\": 10.5}\,\"version\": \"1.0\"}",0,0\r\n
备注:
1、topic从阿里云平台获取
2、msg有一定的格式,在阿里云平台的开发者文档里也有详细的说明,附上文档连接:
阿里云物联网平台设备上报属性
示例中上报的属性为:温度10.5。
3、使用这个指令遇到" “ ”的时候,学会使用专业字符“\”。
串口回复:
AT+MQTTPUB=0"/sys/k0efkfcSwlt/Device_Demo/thing/event/property/post""{\"params\": {\"temperature\": 10.5}\\"version\": \"1.0\"}"00OK
我们查看阿里云物联网平台物模型数据,可以看到阿里云已成功接收数据。
(8)订阅数据
使用指令:AT+MQTTSUB=0,“topic”,0\r\n
示例:AT+MQTTSUB=0,"/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set",0
备注:topic从阿里云物联网平台获取
串口回复:
AT+MQTTSUB=0"/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set"0OK
这时,我们通过在线调试测试订阅数据是否成功。
我们在阿里云平台自定义了一盏LED灯,通过在线调试的方式,发送开启和关闭的命令,如果订阅没有问题的话,可以看到如下串口回复:
+MQTTSUBRECV:0"/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set"100{"method":"thing.service.property.set""id":"2001671834""params":{"LEDSwitch":0}"version":"1.0.0"}
+MQTTSUBRECV:0"/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set"100{"method":"thing.service.property.set""id":"2046606498""params":{"LEDSwitch":1}"version":"1.0.0"}
可以看到json数据里LEDSwitch的变换,这时,我们就可以在单片机上,处理这条指令来实现LED的亮灭,实现阿里云物联网平台控制led。
如果顺利到这一步,那么恭喜你,现在距离成功仅剩一半的距离了,接下来只需将AT指令封装到STM32的代码中,进行简单的STM32串口操作,就可以实现使用STM32连接阿里云的操作了。
四、STM32环形串口缓冲区驱动程序
环形串口缓冲区的原理可以查看这篇文章:环形串口缓冲区
明白了环形串口缓冲区的原理,在STM32开发中,先写好串口的驱动程序,然后再编写环形串口缓冲区程序,下面是驱动程序,我都有注释,学过STM32的都能看得懂。
usart.c
/********************************************************************************* @file usart.c* @author 小途* @version V1.0* @date 2022-2-21* @brief STM32F1的串口初始化设置******************************************************************************* @attention* 串口2配置环形串口缓冲区********************************************************************************/#include "usart.h"#define UART_RX_MAX 255 //数组最大值//环形缓冲区结构体
typedef struct
{uint8_t head_count; //头计数uint8_t tail_count; //尾计数uint8_t buf[UART_RX_MAX]; //缓冲区数组
}UART_S;UART_S uart;//定义串口缓冲区结构体变量//串口环形缓冲区初始化
void uart_Buffer_Init(void)
{int i;uart.head_count = 0;uart.tail_count = 0;for(i=0; i<UART_RX_MAX; i++){uart.buf[i] = 0x00;}
}//往缓冲器里放数据
void uart_buf_put(uint8_t ch)
{uart.buf[uart.head_count++] = ch;if(uart.head_count == UART_RX_MAX){uart.head_count = 0;}
}//判断缓冲区是否有数据
int fifo_is_empty(void)
{if(uart.head_count != uart.tail_count)//判断如果头不等于尾{return 0;//有数据}else return 1;//无数据
}//从缓冲区取数据
int uart_buf_get(uint8_t *ch)
{if(fifo_is_empty() == 0)//如果有数据{*ch = uart.buf[uart.tail_count++];//取数据if(uart.tail_count == UART_RX_MAX){uart.tail_count = 0;} return 1;//返回成功}else return 0;//返回失败
}//读数据,将数据通过串口发送出去
void debug_read(void)
{uint8_t ch;/*如果有数据就一直取,直到取完为至*/while(!fifo_is_empty()){if(uart_buf_get(&ch)){putchar(ch);}}
}/*** @brief 配置嵌套向量中断控制器NVIC* @param 无* @retval 无*/
void NVIC_Configuration(void)
{NVIC_InitTypeDef NVIC_InitStructure;/* 嵌套向量中断控制器组选择 */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);/* 配置USART为中断源 */NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;/* 抢断优先级*/NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;/* 子优先级 */NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;/* 使能中断 */NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;/* 初始化配置NVIC */NVIC_Init(&NVIC_InitStructure);
}//对串口2配置嵌套向量中断控制器NVIC
void NVIC_USART2_Configuration(void)
{NVIC_InitTypeDef NVIC_InitStructure;/* 嵌套向量中断控制器组选择 */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);/* 配置USART为中断源 */NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART2_IRQ;/* 抢断优先级*/NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;/* 子优先级 */NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;/* 使能中断 */NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;/* 初始化配置NVIC */NVIC_Init(&NVIC_InitStructure);
}/*** @brief USART GPIO 配置,工作参数配置* @param 无* @retval 无*/
void USART_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;// 打开串口GPIO的时钟DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);// 打开串口外设的时钟DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);// 将USART Tx的GPIO配置为推挽复用模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);// 将USART Rx的GPIO配置为浮空输入模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);// 配置串口的工作参数// 配置波特率USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;// 配置 针数据字长USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 配置停止位USART_InitStructure.USART_StopBits = USART_StopBits_1;// 配置校验位USART_InitStructure.USART_Parity = USART_Parity_No ;// 配置硬件流控制USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 配置工作模式,收发一起USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 完成串口的初始化配置USART_Init(DEBUG_USARTx, &USART_InitStructure);// 串口中断优先级配置//NVIC_Configuration();// 使能串口接收中断//USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE); // 使能串口USART_Cmd(DEBUG_USARTx, ENABLE);
}/*** @brief USART2 GPIO 配置,工作参数配置,配置环形串口缓冲区* @param 无* @retval 无*/
void USART2_Config(void)
{//在串口2配置环形串口缓冲区//初始化环形串口缓冲区uart_Buffer_Init();GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;// 打开串口GPIO的时钟DEBUG_USART2_GPIO_APBxClkCmd(DEBUG_USART2_GPIO_CLK, ENABLE);// 打开串口外设的时钟DEBUG_USART2_APBxClkCmd(DEBUG_USART2_CLK, ENABLE);// 将USART Tx的GPIO配置为推挽复用模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART2_TX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(DEBUG_USART2_TX_GPIO_PORT, &GPIO_InitStructure);// 将USART Rx的GPIO配置为浮空输入模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART2_RX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(DEBUG_USART2_RX_GPIO_PORT, &GPIO_InitStructure);// 配置串口的工作参数// 配置波特率USART_InitStructure.USART_BaudRate = DEBUG_USART2_BAUDRATE;// 配置 针数据字长USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 配置停止位USART_InitStructure.USART_StopBits = USART_StopBits_1;// 配置校验位USART_InitStructure.USART_Parity = USART_Parity_No ;// 配置硬件流控制USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 配置工作模式,收发一起USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 完成串口的初始化配置USART_Init(DEBUG_USART2, &USART_InitStructure); //串口2中断优先级配置NVIC_USART2_Configuration();//使能串口2接收中断USART_ITConfig(DEBUG_USART2, USART_IT_RXNE, ENABLE);// 使能串口//USART_Cmd(DEBUG_USART2, ENABLE);
}/***************** 发送一个字符 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{/* 发送一个字节数据到USART */USART_SendData(pUSARTx,ch);/* 等待发送数据寄存器为空 */while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}/***************** 发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{unsigned int k=0;do {Usart_SendByte( pUSARTx, *(str + k) );k++;} while(*(str + k)!='\0');/* 等待发送完成 */while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}//发送整形数据字符串(发送长字符串)
void Usart_SendIntString( USART_TypeDef * pUSARTx, int *str)
{int k=0;do {Usart_SendByte( pUSARTx, *(str + k) );k++;} while(*(str + k)!='\0');/* 等待发送完成 */while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}//重定向c库函数printf到串口,重定向后可使用printf函数,默认使用串口1
int fputc(int ch, FILE *f)
{/* 发送一个字节数据到串口 */USART_SendData(DEBUG_USART1, (uint8_t) ch);/* 等待发送完毕 */while (USART_GetFlagStatus(DEBUG_USART1, USART_FLAG_TXE) == RESET); return (ch);
}//重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数,默认使用串口1
int fgetc(FILE *f)
{/* 等待串口输入数据 */while (USART_GetFlagStatus(DEBUG_USART1, USART_FLAG_RXNE) == RESET);return (int)USART_ReceiveData(DEBUG_USART1);
}
usart.h
#ifndef __USART_H
#define __USART_H#include "stm32f10x.h"
#include <stdio.h>/** 串口宏定义,不同的串口挂载的总线不一样,移植时需要修改这几个宏*/// 串口1-USART1
#define DEBUG_USARTx USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10#define DEBUG_USART_IRQ USART1_IRQn
#define DEBUG_USART_IRQHandler USART1_IRQHandler//------------------------------------------------------------------------// 串口1-USART1
#define DEBUG_USART1 USART1
#define DEBUG_USART1_CLK RCC_APB2Periph_USART1
#define DEBUG_USART1_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART1_BAUDRATE 115200// USART GPIO 引脚宏定义
#define DEBUG_USART1_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART1_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd#define DEBUG_USART1_TX_GPIO_PORT GPIOA
#define DEBUG_USART1_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART1_RX_GPIO_PORT GPIOA
#define DEBUG_USART1_RX_GPIO_PIN GPIO_Pin_10#define DEBUG_USART1_IRQ USART1_IRQn
#define DEBUG_USART1_IRQHandler USART1_IRQHandler//------------------------------------------------------------------------// 串口2-USART2
#define DEBUG_USART2 USART2
#define DEBUG_USART2_CLK RCC_APB1Periph_USART2
#define DEBUG_USART2_APBxClkCmd RCC_APB1PeriphClockCmd
#define DEBUG_USART2_BAUDRATE 115200// USART GPIO 引脚宏定义
#define DEBUG_USART2_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART2_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd#define DEBUG_USART2_TX_GPIO_PORT GPIOA
#define DEBUG_USART2_TX_GPIO_PIN GPIO_Pin_2
#define DEBUG_USART2_RX_GPIO_PORT GPIOA
#define DEBUG_USART2_RX_GPIO_PIN GPIO_Pin_3#define DEBUG_USART2_IRQ USART2_IRQn
#define DEBUG_USART2_IRQHandler USART2_IRQHandler//使能串口2
#define USART2_ON USART_Cmd(DEBUG_USART2, ENABLE);
//失能串口2
#define USART2_OFF USART_Cmd(DEBUG_USART2, DISABLE);//环形串口缓冲区相关功能函数
void uart_buf_put(uint8_t ch);
void debug_read(void);
int fifo_is_empty(void);
int uart_buf_get(uint8_t *ch);//STM32串口相关功能函数
void USART_Config(void);
void USART2_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendIntString( USART_TypeDef * pUSARTx, int *str);#endif
stm32f10x_it.c
//串口2中断服务函数
//将串口2接收到的数据回传到串口1
void USART2_IRQHandler(void)
{uint8_t ucTemp;if(USART_GetITStatus(DEBUG_USART2,USART_IT_RXNE)!=RESET){ ucTemp = USART_ReceiveData(DEBUG_USART2);uart_buf_put(ucTemp);//往环形串口缓冲区存数据}
}
五、STM32连接阿里云驱动程序
STM32连接阿里云程序逻辑与串口助手的一致,之前使用串口助手与我的电脑进行数据交换,现在将STM32当成一个小型的“电脑”,使用STM32来对ESP8266的数据进行处理,达到物联网控制的目的。
我使用的是STM32霸道的开发板做的测试例程, 使用杜邦线连接ESP8266模块,以下的厂程序供大家参考,如有问题望指摘。
esp8266.c
/********************************************************************************* @file esp8266.c* @author fire* @version V1.0* @date 2024-1-16* @brief esp8266连接阿里云物联网平台******************************************************************************
**/#include "./ESP8266/esp8266.h"//测试esp8266是否存在
//向串口2发送“AT”,esp8266收到后会回复一个“Ok”
//STM32会将串口2收到的数据转发到串口1,用于监视串口2状态
//函数需要提前初始化串口1,串口2,串口2中断接收
//成功读取到esp8266返回1,否则返回0
char esp8266_text(void)
{uint8_t ch;uint8_t str[20], i=0;char* pch;//发送测试命令Usart_SendString(DEBUG_USART2, "AT\r\n");//延时一段时间等待接收Delay_ms(50);//向串口环形缓冲区取数据while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}//STM32将连接在串口2上ESP8266发送过来的数据转发到串口1
// Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");
// Usart_SendString(DEBUG_USART1, (char *)str);
// Usart_SendString(DEBUG_USART1, "\r\n");pch = strstr((char *)str, "OK");if(pch != NULL){Usart_SendString(DEBUG_USART1, "esp8266真实存在\r\n");return 1;}elsereturn 0;
}//esp8266重启
void esp8266_restart(void)
{uint8_t ch;uint8_t str[255], i=0;//发送命令Usart_SendString(DEBUG_USART2, "AT+RST\r\n");//延时一段时间等待接收Delay_ms(50);//向串口环形缓冲区取数据while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}//STM32将连接在串口2上ESP8266发送过来的数据转发到串口1Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");Usart_SendString(DEBUG_USART1, (char *)str);Usart_SendString(DEBUG_USART1, "\r\n");
}//将esp8266设置为接入点模式
//设置成功返回1,失败返回0
char esp8266_setStation(void)
{uint8_t ch;uint8_t str[20], i=0; char* pch;//发送命令Usart_SendString(DEBUG_USART2, "AT+CWMODE=1\r\n");//延时一段时间等待接收Delay_ms(50);//向串口环形缓冲区取数据while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}//STM32将连接在串口2上ESP8266发送过来的数据转发到串口1
// Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");
// Usart_SendString(DEBUG_USART1, (char *)str);
// Usart_SendString(DEBUG_USART1, "\r\n");pch = strstr((char *)str, "OK");if(pch != NULL)return 1;elsereturn 0;
}//查询WiFi连接信息
//如果成功连接WiFi返回1,否则返回0
char esp8266_inquireWiFi_Data(void)
{uint8_t ch;uint8_t str[255], i=0;char* pch;//发送命令Usart_SendString(DEBUG_USART2, "AT+CWSTATE?\r\n");//延时一段时间等待接收Delay_ms(50);//向串口环形缓冲区取数据while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}//STM32将连接在串口2上ESP8266发送过来的数据转发到串口1
// Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");
// Usart_SendString(DEBUG_USART1, (char *)str);
// Usart_SendString(DEBUG_USART1, "\r\n");pch = strchr((char *)str, '2');if(pch != NULL)return 1;elsereturn 0;
}//连接WiFi
void esp8266_connectWiFi(void)
{uint8_t ch, i=0;char str[255];//将要发送的数据拼接起来char wifi_data[255] = "AT+CWJAP=\"";strcat(wifi_data, WiFi_Name);strcat(wifi_data, "\",\"");strcat(wifi_data, WiFi_Password);strcat(wifi_data, "\"\r\n");//发送命令Usart_SendString(DEBUG_USART2, wifi_data);//延时一段时间等待接收Delay_ms(50);//向串口环形缓冲区取数据while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}//STM32将连接在串口2上ESP8266发送过来的数据转发到串口1Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");Usart_SendString(DEBUG_USART1, str);Usart_SendString(DEBUG_USART1, "\r\n");
}//设置MQTT连接参数
//设置成功返回1,否则返回0
char esp8266_setMQTTData(void)
{uint8_t ch;uint8_t str[255], i=0;char* pch;//发送命令Usart_SendString(DEBUG_USART2, "AT+MQTTUSERCFG=0,1,\"");Usart_SendString(DEBUG_USART2, Aliyun_ClientId);Usart_SendString(DEBUG_USART2, "\",\"");Usart_SendString(DEBUG_USART2, Aliyun_Username);Usart_SendString(DEBUG_USART2, "\",\"");Usart_SendString(DEBUG_USART2, Aliyun_Password);Usart_SendString(DEBUG_USART2, "\",0,0,\"\"\r\n");//延时一段时间等待接收Delay_ms(50);//向串口环形缓冲区取数据while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}// Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");
// Usart_SendString(DEBUG_USART1, (char *)str);
// Usart_SendString(DEBUG_USART1, "\r\n");pch = strstr((char *)str, "OK");if(pch != NULL)return 1;elsereturn 0;
}//连接阿里云
//连接成功回复1,失败回复0
char connect_Aliyun(void)
{uint8_t ch;uint8_t str[255], i=0;char* pch;//检查esp8266是否存在if(esp8266_text() != 1){printf("连接错误,代码1:请检测esp8266与芯片连接线路!\r\n");return 0;}//设置esp8266为接入点模式if(esp8266_setStation() != 1){printf("连接错误,代码2:esp8266 接入点模式设置失败,请检查!\r\n");return 0;}else{printf("接入点模式设置成功!\r\n");}//连接WiFi网络//连接之前先检查WiFi网络是否连接//如果以连接则跳过此步骤if(esp8266_inquireWiFi_Data() != 1){//连接WiFiesp8266_connectWiFi();printf("正在连接WiFi");//5(s)*15=60(s)=1(min)//等待一段时间,待WiFi连接成功i=15;while(1){Delay_ms(5000);printf(".");//如果WiFi成功退出循环if(esp8266_inquireWiFi_Data() == 1){printf("WiFi连接成功!\r\n");break;}//超时连接退出程序i--;if(i == 0){printf("\r\n错误代码3:WiFi连接超时,已退出!\r\n");return 0;}}}else{printf("WiFi连接成功!\r\n");}//设置MQTT连接参数if(esp8266_setMQTTData() != 1){printf("连接错误,代码4:esp8266 接入点模式设置失败,请检查!\r\n");return 0;}else{printf("MQTT连接参数设置成功!\r\n");}//-------------------------------------------------------------------------------------------for(i=0; i<255; i++){str[i] = '\0';}i=0;//下面开始连接阿里云//发送命令Usart_SendString(DEBUG_USART2, "AT+MQTTCONN=0,\"");Usart_SendString(DEBUG_USART2, BrokerAddress);Usart_SendString(DEBUG_USART2, "\",1883,1\r\n");Delay_ms(50);//向串口环形缓冲区取数据while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}// Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");
// Usart_SendString(DEBUG_USART1, (char *)str);
// Usart_SendString(DEBUG_USART1, "\r\n");// for(i=0; i<255; i++)
// {
// str[i] = '\0';
// }
//
// i=0;//查询阿里云连接状态Usart_SendString(DEBUG_USART2, "AT+MQTTCONN?");Delay_ms(50);//向串口环形缓冲区取数据while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}Usart_SendString(DEBUG_USART1, "阿里云的连接状态为:\r\n");Usart_SendString(DEBUG_USART1, (char *)str);Usart_SendString(DEBUG_USART1, "\r\n");pch = strstr((char *)str, BrokerAddress);if(pch != NULL){printf("阿里云物联网平台连接成功!\r\n");return 1;}else{printf("错误代码6:阿里云物联网平台连接失败,请检查!\r\n");return 0;}
}//向阿里云上传温度数据
void AliyunSetData(float temperature)
{uint8_t ch;uint8_t str[255], i=0;//发送数据Usart_SendString(DEBUG_USART2, "AT+MQTTPUB=0,\"");Usart_SendString(DEBUG_USART2, IssueTOPIC);Usart_SendString(DEBUG_USART2, "\",\"{\\\"params\\\": {\\\"temperature\\\":");//将浮点型数据转换成字符串并发送出去char str2[10];sprintf(str2,"%f", temperature);Usart_SendString(DEBUG_USART2, str2);Usart_SendString(DEBUG_USART2, "}\\,\\\"version\\\": \\\"1.0\\\"}\",0,0\r\n");Delay_ms(50);//向串口环形缓冲区取数据while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}Usart_SendString(DEBUG_USART1, "收到ESP8266传来的数据:\r\n");Usart_SendString(DEBUG_USART1, (char *)str);Usart_SendString(DEBUG_USART1, "\r\n");
}//阿里云订阅数据
void AliyunSubscriptionData(void)
{ uint8_t ch;uint8_t str3[255], i=0;Usart_SendString(DEBUG_USART2, "AT+MQTTSUB=0,\"");Usart_SendString(DEBUG_USART2, SubscriptionTOPIC);Usart_SendString(DEBUG_USART2, "\",0\r\n");Delay_ms(50);//向串口环形缓冲区取数据while(!fifo_is_empty()){if(uart_buf_get(&ch)){str3[i] = ch;i++;}}Usart_SendString(DEBUG_USART1, "\r\n");Usart_SendString(DEBUG_USART1, "esp8266设置订阅数据回复:\r\n");Usart_SendString(DEBUG_USART1, (char *)str3);Usart_SendString(DEBUG_USART1, "\r\n");
}
esp8266.h
#ifndef __ESP8266_H
#define __ESP8266_H#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
#include <string.h>#define WiFi_Name "CMCC-H3qz"
#define WiFi_Password "7xf47uxf"
#define Aliyun_ClientId "k0efkfcSwlt.Device_Demo|securemode=2\\,signmethod=hmacsha256\\,timestamp=1705149361131|"
#define Aliyun_Username "Device_Demo&k0efkfcSwlt"
#define Aliyun_Password "ea343ac1d6a4eec7b057bcab39a2ffdf88abc8b810ec0dc7ca3de91a56292ff9"
#define BrokerAddress "k0efkfcSwlt.iot-as-mqtt.cn-shanghai.aliyuncs.com"//发布数据topic
#define IssueTOPIC "/sys/k0efkfcSwlt/Device_Demo/thing/event/property/post"
//订阅数据topic
#define SubscriptionTOPIC "/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set"char esp8266_text(void);
void esp8266_restart(void);
char esp8266_setStation(void);
char esp8266_inquireWiFi_Data(void);
void esp8266_connectWiFi(void);
char esp8266_setMQTTData(void);
char connect_Aliyun(void);
void AliyunSetData(float temperature);
void AliyunSubscriptionData(void);#endif
main.c
void RCC_Configuration(void)
{ //Install system time is 72MHZSystemInit();
}int main(void)
{ uint8_t ch, bufferFlag=0;char str[255], i=0;char *property = 0;char led_switch;RCC_Configuration(); //系统时钟初始化Delay_Timer_Init(); //延时函数定时器初始化USART_Config(); //串口1初始化USART2_Config(); //串口2初始化//向串口1 发送一段数据,测试串口1初始化成功printf("这是一个STM32挂载ESP8266连接阿里云的例程!\r\n");//初始化PB5为输出gpio_OutputConfig(RCC_APB2Periph_GPIOB, GPIO_Pin_5, GPIOB);//并将LED灯熄灭(将引脚拉高)GPIO_SetBits(GPIOB, GPIO_Pin_5);//如下同理...gpio_OutputConfig(RCC_APB2Periph_GPIOB, GPIO_Pin_0, GPIOB);GPIO_SetBits(GPIOB, GPIO_Pin_0);//初始化PA0为按键功能(SW1)Key_GPIO_Config(RCC_APB2Periph_GPIOA, GPIO_Pin_0, GPIOA);//初始化PC13为按键功能(SW2)Key_GPIO_Config(RCC_APB2Periph_GPIOC, GPIO_Pin_13, GPIOC);while(1){//sw1按键按下功能if(Key_Scan(GPIOA, GPIO_Pin_0) == 1){//使能串口2USART2_ON;//连接阿里云if(connect_Aliyun() == 1){if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5) == 1){GPIO_ResetBits(GPIOB, GPIO_Pin_5); //红灯亮起AliyunSubscriptionData(); //订阅数据} else{GPIO_SetBits(GPIOB, GPIO_Pin_5); //灯熄灭USART2_ON; //使能串口2}}}//sw2按键按下功能//判断esp8266是否存在if(Key_Scan(GPIOC, GPIO_Pin_13) == 1){USART2_ON;esp8266_text();}//检测到红灯亮起时,发布和订阅数据if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5) == 0){//每隔5秒发布数据Delay_ms(1000);AliyunSetData(1.2);//接收阿里云发送过来的数据for(int j=0; j<5; j++){//每隔1秒查看串口环形缓冲区是否有数据Delay_ms(1000);for(i=0; i<127; i++){str[i] = '\0';}i=0;//向串口环形缓冲区取数据while(!fifo_is_empty()){bufferFlag = 1;if(uart_buf_get(&ch)){str[i] = ch;i++;}}if(bufferFlag == 1){bufferFlag = 0;//STM32将连接在串口2上ESP8266发送过来的数据转发到串口1Usart_SendString(DEBUG_USART1, "收到阿里云服务器发送过来的数据:\r\n");Usart_SendString(DEBUG_USART1, str);Usart_SendString(DEBUG_USART1, "\r\n");//以下是通过阿里云控制一盏led灯例程property = strstr(str, "LEDSwitch");if(property != NULL){led_switch = *(property + 11);//Usart_SendByte(DEBUG_USART1, led_switch);led_switch = led_switch - 48;if(led_switch == 0){GPIO_SetBits(GPIOB, GPIO_Pin_0);}else if(led_switch == 1){GPIO_ResetBits(GPIOB, GPIO_Pin_0);}}}}}}
}
相关文章:

STM32连接阿里云物联网平台
文章目录 引言一、STM32连接阿里云物联网平台思路二、ESP8266烧录固件三、使用AT指令连接阿里云物联网平台四、STM32环形串口缓冲区驱动程序五、STM32连接阿里云驱动程序 引言 连续写了两篇关于阿里云连接的文章,都是使用Arduino ESP8266 & Arduino ESP32的方式…...

力扣hot100 组合总和 回溯 剪枝 组合
Problem: 39. 组合总和 文章目录 思路复杂度💖 Code 思路 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( n ) O(n) O(n) 💖 Code class Solution{List<List<Integer>> res new ArrayList<>();int x;// 全局targetin…...

代码随想录 Leetcode669. 修剪二叉搜索树
题目: 代码(首刷看解析 2024年1月31日): class Solution { public:TreeNode* trimBST(TreeNode* root, int low, int high) {if (!root) return root;if (root->val < low) {TreeNode* node trimBST(root->right,low,high);return…...

Redis系列-数据结构篇
数据结构 string(字符串) redis的字符串是动态字符串,类似于ArrayList,采用预分配冗余空间的方式减少内存的频繁分配。 struct SDS<T>{ T capacity; T len; byte flags; byte[] content; } 当字符串比较短时,…...

正则表达式(RE)
什么是正则表达式 正则表达式,又称规则表达式(Regular Expression)。正则表达式通常被用来检索、替换那些符合某个规则的文本 正则表达式的作用 验证数据的有效性替换文本内容从字符串中提取子字符串 匹配单个字符 字符功能.匹配任意1个…...

发布技术路线图!美国量子计算公司QuEra公开三年OKR
编辑丨慕一 编译/排版丨琳梦 卉可 深度好文:1100字丨8分钟阅读 近期,美国量子计算公司QuEra Computing宣布了一系列关于容错量子计算机的战略路线图,该路线图从2024年开始,最终目标是打造具有100纠错逻辑量子比特的系统。 在…...
Vue2:请求接口的两种方式axios和vue-resource
一、场景描述 前端和后端的交互,肯定是要发生接口调用的 这个时候,就要涉及前端如何向后端接口发送请求,获取数据 二、请求方式 1、axios方式(推荐) 这个方式本质就是ajax,底层就是对xhr(XMLHttpRequest)的封装 1、安装axios…...

扩展学习|商业智能和大数据分析的研究前景(比对分析)
文献来源: Liang T P , Liu Y H .Research Landscape of Business Intelligence and Big Data analytics: A bibliometrics study[J].Expert Systems with Applications, 2018, 111(NOV.):2-10.DOI:10.1016/j.eswa.2018.05.018. 信息和通信技术的快速发展导致了数字…...

『Docker入门指南』- 详细安装与配置教程,助你起航容器化世界!
引言 在探索云计算和自动化部署的时代,Docker以其独特的容器化技术站在了风口浪尖。如果你期待着无缝地将你的应用从一个环境迁移到另一个环境,那么Docker无疑是你的得力助手。但首先,我们得学会如何正确地安装和配置Docker。这篇文章将详细…...
如何提高http连接成功率?
问题 丢包、错包、乱包 高延迟 响应数据回来时间长,甚至大于客户端等待时间 带宽小 每次能够通信的内容较少,数据包越大受影响可能越大 网络断续 网络经常断开又连接 优化处理 采用TCP协议、实现长连接,采用长连接池,节省…...
Elasticsearch 中使用MustNot等同于不等于遇到的坑
1、在写关键词推荐时,需要把当前文章过滤掉,不能再推荐自己的文章,所以再es中需要用到 MustNot属性查询 /// <summary> /// 服务中心es检索 /// </summary> /// <param name="input"></param> /// <returns></…...

嵌入式工程师day15(链表)
内存管理 一.内存管理: 1.malloc void *malloc(size_t size); 功能: 申请堆区空间 参数: size:申请堆区空间的大小 返回值: 返回获得的空间的首地址 失败返回NULL 2.free void free(void *ptr); 功能: 释放…...

Coppeliasim倒立摆demo
首先需要将使用Python远程控制的文件导入到文件夹,核心是深蓝色的三个文件。 本版本为4.70,其文件所在位置如下图所示,需要注意的是,目前不支持Ubuntu22的远程api: 双击Sphere这一行的灰色文件,可以看到远程…...

汽车燃油泵数据分析:全球市场的年复合增长率将达到10%左右
燃油泵是汽车配件行业的专业术语。是电喷汽车燃油喷射系统的基本组成之一,位于车辆油箱内部,燃油泵在启动和发动机运转时工作,如果发动机停止而点火开关仍处于ON时,HFM-SFI控制模块关闭燃油泵的电源,以避免意外点火。 …...
DC-磁盘管理(23国赛真题)
2023全国职业院校技能大赛网络系统管理赛项–模块B:服务部署(WindowServer2022) 文章目录 题目配置步骤组成RAID 5,磁盘分区命名为卷标H盘:Raid5。手动测试破坏一块磁盘,做RAID磁盘修复,确认RAID 5配置完毕。验证查看Raid5(打开磁盘管理器,查看raid信息)Raid5磁盘修复…...

216961-98-7,BODIPY 493/503 NHS 活化酯,可以应用于分子生物学等领域中
您好,欢迎来到新研之家 文章关键词:216961-98-7,BODIPY 493/503 NHS 活化酯,BODIPY 493/503 NHS ester,BODIPY 493/503 SE 一、基本信息 产品简介:BODIPY 493/503 NHS ester是一种特殊的染料,…...

Python采集学习笔记-读取excel数据
表格格式 方法一:使用xlrd import xlrd 1.读取Excel文件 workbook xlrd.open_workbook(plc.xlsx) 2.读取第一个表 sheet workbook.sheet_by_index(0) 3.获取表格总行数 total_rows sheet.nrows 4.创建列表,存储表格一行中每一列信息 plc_info [] for row in range(1…...

幻兽帕鲁服务器游戏怎么升级版本?
幻兽帕鲁服务器游戏怎么升级版本?自建幻兽帕鲁服务器进入Palworld游戏提示“您正尝试加入的比赛正在运行不兼容的游戏版本,请尝试升级游戏版本”什么原因?这是由于你的客户端和幻兽帕鲁服务器版本不匹配,如何解决?更新…...
【ASP.NET Core 基础知识】--身份验证和授权--授权和策略
一、授权和策略的概念及应用 在ASP.NET Core中,授权和策略是重要的安全概念,用于确定用户是否有权限执行特定的操作或访问特定的资源。以下是关于ASP.NET Core中授权和策略的概念及其应用的一些重要信息: 1.1 授权(Authorizatio…...

20240130在ubuntu20.04.6下卸载NVIDIA显卡的驱动
20240130在ubuntu20.04.6下卸载NVIDIA显卡的驱动 2024/1/30 12:58 缘起,为了在ubuntu20.4.6下使用whisper,以前用的是GTX1080M,装了535的驱动。 现在在PDD拼多多上了入手了一张二手的GTX1080,需要将安装最新的545的驱动程序&#…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
手动给中文分词和 直接用神经网络RNN做有什么区别
手动分词和基于神经网络(如 RNN)的自动分词在原理、实现方式和效果上有显著差异,以下是核心对比: 1. 实现原理对比 对比维度手动分词(规则 / 词典驱动)神经网络 RNN 分词(数据驱动)…...

【Linux】使用1Panel 面板让服务器定时自动执行任务
服务器就是一台24小时开机的主机,相比自己家中不定时开关机的主机更适合完成定时任务,例如下载资源、备份上传,或者登录某个网站执行一些操作,只需要编写 脚本,然后让服务器定时来执行这个脚本就可以。 有很多方法实现…...

从数据报表到决策大脑:AI重构电商决策链条
在传统电商运营中,决策链条往往止步于“数据报表层”:BI工具整合历史数据,生成滞后一周甚至更久的销售分析,运营团队凭经验预判需求。当爆款突然断货、促销库存积压时,企业才惊觉标准化BI的决策时差正成为增长瓶颈。 一…...
【向量库】Weaviate 搜索与索引技术:从基础概念到性能优化
文章目录 零、概述一、搜索技术分类1. 向量搜索:捕捉语义的智能检索2. 关键字搜索:精确匹配的传统方案3. 混合搜索:语义与精确的双重保障 二、向量检索技术分类1. HNSW索引:大规模数据的高效引擎2. Flat索引:小规模数据…...