物联网实践教程:微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——STM32代码实现篇

STM32代码实现

开启本章节需要完成下方的前置任务:
点击跳转:
 

物联网实践教程:微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——汇总

目标

1.连接OneNET:STM32使用串口与ESP8266/01s连接发送AT指令连接上OneNET MQTT设备

2.数据上报OneNET:将STM32采集的数据上传到OneNET

3.接收OneNET下发数据:STM32接收OneNET下发的指令并进行对应的操作

代码主要逻辑

无线模块状态结构体
 

typedef struct {uint8_t receiveDataFlag;				//接收数据标志位uint8_t sendDataFlag;					//发送数据标志位uint8_t wirelessInitFlag;				//无线模块初始化完成标志位uint16_t wirelessInitErrorCode;			//无线模块初始化错误代码
}Wireless_TypeDef;extern Wireless_TypeDef WirelessStatus;


无线模块连接OneNET MQTT设备需要发送的AT指令

全部使用宏定义,方便其他的用户直接修改设备等相关信息即可直接使用

#include "wireless.h"/*只需更新这些信息
*/#define JsonEnable 1Wireless_TypeDef WirelessStatus = {0, 0, 0, 0};#define WIFI_SSID "207"				//WIFI用户名#define WIFI_PASSWORD "12345678"		//WIFI密码#define ONENET_MQTT_PRODUCT_ID "YqRZ5hrM6p"		//OneNET MQTT产品ID#define ONENET_MQTT_DEVICE_NAME "CSDN"			//OneNET MQTT设备名称#define ONENET_MQTT_TOKEN "version=2018-10-31&res=products%2FYqRZ5hrM6p%2Fdevices%2FCSDN&et=2028715245&method=md5&sign=G4I0xqIYmYUtCdTTo2t%2FqQ%3D%3D"		//token#define ONENET_MQTT_CMD_IDENTIFIER "cmd"			//OneNET MQTT命令标识符const char *floatIdentifiers[] = {"temp", "bright"};    //需要上报数据的整形标识符		
float floatValues[] = {23.7, 55.6};                     //上报数据对应的整形大小
const char *stringIdentifiers[] = {0};		            //需要上报数据的字符串标识符	
const char *stringValues[] = {0};	                    //上报数据对应的字符串/*只需更新这些信息*/
#define WIRELESS_WIFI_INFO "AT+CWJAP=\"" WIFI_SSID "\",\"" WIFI_PASSWORD "\"\r\n"
#define WIRELESS_RECEIVE_CMD "\"" ONENET_MQTT_CMD_IDENTIFIER "\":"#define ONENET_MQTT_SERVER_INFO "AT+MQTTCONN=0,\"mqtts.heclouds.com\",1883,1\r\n"
#define ONENET_MQTT_USERCFG_INFO "AT+MQTTUSERCFG=0,1,\"" ONENET_MQTT_DEVICE_NAME "\",\"" ONENET_MQTT_PRODUCT_ID "\",\"" ONENET_MQTT_TOKEN "\",0,0,\"\"\r\n"
#define ONENET_MQTT_SET_TOPIC "AT+MQTTSUB=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/set\",0\r\n"#if JsonEnable#define ONENET_MQTT_SET_MQTTPUBRAW "AT+MQTTPUBRAW=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/set_reply\""#define ONENET_MQTT_PUB_MQTTPUBRAW "AT+MQTTPUBRAW=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/post\""
#else#define ONENET_MQTT_PUB_SET "AT+MQTTPUB=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/set_reply\""#define ONENET_MQTT_PUBTOPIC "AT+MQTTPUB=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/post\""
#endif

1.初始化

由前置任务可以知道,每次发送AT指令后,无线模块总是会回应我们,例如“OK”又或者其他的内容,这里初始化发送的每条AT指令都必须要得到无线模块的回应,这样防止无线模块未按照预期模式运行,如果初始化出现错误时,需要即使去处理对应的问题

这里如果不了解为什么要这样写代码的逻辑,放个传送门:
物联网实践教程:微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——ESP8266/01s AT指令连接OneNET MQTT篇
 

/**  * @简要  无线模块初始化函数* @参数  无* @注意事项 	如果函数初始化失败,会进入错误函数* @返回值 无  */
void Wireless_Init(void)
{const uint8_t sendDataCount = 10;printf("\r\nStart MQTT service\r\n");printf("1. AT+RST\r\n");    										//需要延迟最少500ms,否则下一句指令发送无效if(Wireless_Send_Command("AT+RST\r\n\r\n", "", sendDataCount) == 1) 		WirelessStatus.wirelessInitErrorCode |= 1 << 0;HAL_Delay(500);printf("2. ATE0\r\n");												//关闭回显if(Wireless_Send_Command("ATE0\r\n", "OK", sendDataCount) == 1)				WirelessStatus.wirelessInitErrorCode |= 1 << 1;HAL_Delay(100);printf("3. AT+CWAUTOCONN=0\r\n");									//上电不自动连接 APif(Wireless_Send_Command("AT+CWAUTOCONN=0\r\n", "OK", sendDataCount) == 1) 	WirelessStatus.wirelessInitErrorCode |= 1 << 2;HAL_Delay(100);printf("4. AT+CWMODE=1\r\n");										//设置 Station 模式if(Wireless_Send_Command("AT+CWMODE=1\r\n", "OK", sendDataCount) == 1)		WirelessStatus.wirelessInitErrorCode |= 1 << 3;HAL_Delay(100);printf("5. AT+CWDHCP=1,0\r\n");										//启用DHCPif(Wireless_Send_Command("AT+CWDHCP=1,0\r\n", "OK", sendDataCount) == 1)	WirelessStatus.wirelessInitErrorCode |= 1 << 4;HAL_Delay(100);printf("6. AT+CWJAP=\"%s\",\"%s\"\r\n", WIFI_SSID, WIFI_PASSWORD);			//连接APif(Wireless_Send_Command(WIRELESS_WIFI_INFO, "GOT IP", 8) == 1) 			WirelessStatus.wirelessInitErrorCode |= 1 << 5;HAL_Delay(100);printf("7. ESP8266_USERCFG_INFO=%s\r\n", ONENET_MQTT_USERCFG_INFO); 		//设置 MQTT 用户属性if(Wireless_Send_Command(ONENET_MQTT_USERCFG_INFO, "OK", sendDataCount) == 1)	WirelessStatus.wirelessInitErrorCode |= 1 << 6;HAL_Delay(100);printf("8. ESP8266_ONENET_INFO=%s\r\n",ONENET_MQTT_SERVER_INFO);   				//连接 MQTT Brokerif(Wireless_Send_Command(ONENET_MQTT_SERVER_INFO, "OK", sendDataCount) == 1) 				WirelessStatus.wirelessInitErrorCode |= 1 << 7;HAL_Delay(100);printf("9. SET_TOPIC=%s\r\n",ONENET_MQTT_SET_TOPIC);							//设置 MQTT 用户属性if(Wireless_Send_Command(ONENET_MQTT_SET_TOPIC, "OK", sendDataCount) == 1) 				WirelessStatus.wirelessInitErrorCode |= 1 << 8;HAL_Delay(100);if(WirelessStatus.wirelessInitErrorCode == 0) {WirelessStatus.wirelessInitFlag = 1;printf("MQTT service started successfully\r\n");}else {WirelessStatus.wirelessInitFlag = 0;printf("MQTT service failed to start,ERROR CODE:%X\r\n", WirelessStatus.wirelessInitErrorCode);Wireless_Error_Handler(WirelessStatus.wirelessInitErrorCode);  //错误处理函数}}

 在上方代码中:

if(Wireless_Send_Command("AT+RST\r\n\r\n", "", sendDataCount) == 1)

这个函数原型:
 

/**  * @简要  无线模块发送指令并且等待回应数据* @参数  cmd:需要发送的指令字符串地址* @参数  res:需要回应的指令* @参数  sendCount:最大发送指令次数* @返回值 无  */
uint8_t Wireless_Send_Command(char *cmd, char *res, uint8_t sendCount)
{uint8_t status = 1;while(sendCount--){Wireless_Usart_Send(cmd);//printf("cmd :%s",cmd);if(WirelessStatus.receiveDataFlag == 1){if(strstr((const char *)WirelessRx.RxBuffer, res) != NULL)		//若找到关键字{		status = 0;WirelessStatus.receiveDataFlag = 0;Wireless_Buffer_Clear();break;}Wireless_Buffer_Clear();WirelessStatus.receiveDataFlag = 0;}HAL_Delay(200);}return status;
}

这里还需要修改明白一个底层的发送函数:

需要将里面的串口发送修改为自己的
这里面的DMA配置也有文章可以参考:
STM32基于HAL库使用串口+DMA 不定长接收数据 学习记录

/**  * @简要  用来适配无线模块发送字符的函数* @参数  cmd: 传入需要发送字符指针* @返回值 无  */
void Wireless_Usart_Send(char *cmd)
{if (cmd == NULL) return; // 确保指针不为空// 发送字符串// 参数依次为:USART句柄指针、发送数据缓冲区指针、数据字节长度HAL_UART_Transmit_DMA(&huart2, (uint8_t *)cmd, strlen(cmd));
}

串口接收采用了DMA空闲接收回调函数处理(可以参考上方连接配置):

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{if (huart->Instance == USART1){Uart1Rx.RxDataCnt = RX_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);if(Uart1Rx.RxBuffer[Uart1Rx.RxDataCnt - 2] == '\r' && Uart1Rx.RxBuffer[Uart1Rx.RxDataCnt - 1] == '\n'){HAL_UART_Transmit_DMA(&huart1, Uart1Rx.RxBuffer, Uart1Rx.RxDataCnt);}}if (huart->Instance == USART2){WirelessRx.RxDataCnt = RX_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);if(WirelessRx.RxBuffer[WirelessRx.RxDataCnt - 2] == '\r' && WirelessRx.RxBuffer[WirelessRx.RxDataCnt - 1] == '\n'){WirelessStatus.receiveDataFlag = 1;if(WirelessStatus.wirelessInitFlag == 1) printf("Receive\r\n");//HAL_UART_Transmit_DMA(&huart1, WirelessRx.RxBuffer, WirelessRx.RxDataCnt);}}
}


 

2.上报数据

这里开启了一个定时器,用一个LED灯去显示当前wifi模块状态,同时也在后台计时,准备设置发送数据标志位

这个定时器的存在只是为了加强用户交互,显示当前WiFi模块的工作状态,例如连接OneNET初始化失败,正在连接,成功连接等等,如果不需要的话完全可以删除,然后发送计时可以在主函数用一个变量累加到一定数量时触发发送

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{static uint16_t countTimer_SendData_OneNET = 0;static uint16_t countTimer_LED_Toggle = 0;if (htim == (&htim1)){if(countTimer_SendData_OneNET++ > 4000){countTimer_SendData_OneNET = 0;WirelessStatus.sendDataFlag = 1;}if(countTimer_LED_Toggle++ >= 1000){countTimer_LED_Toggle = 0;if(WirelessStatus.wirelessInitErrorCode == 0 && WirelessStatus.wirelessInitFlag == 0)HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); //正在初始化else if(WirelessStatus.wirelessInitErrorCode == 0 && WirelessStatus.wirelessInitFlag == 1)HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);		//初始化成功else if(WirelessStatus.wirelessInitErrorCode != 0)HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);	//初始化失败}}
}

这里是整个无线模块的运行函数

/**  * @简要  无线模块运行函数	* @参数  无* @注意事项	一直轮询,可以设置为定时触发,但是回应下发的数据会变慢* @返回值 无  */
void Wireless_Loop(void)
{if(WirelessStatus.receiveDataFlag == 1){WirelessStatus.receiveDataFlag = 0;	//清除接收数据标志位Wireless_Receive_Command_Respond((char *)WirelessRx.RxBuffer);		//接收到指令后进行回应操作Wireless_Buffer_Clear();     //执行完接收数据的所有操作后再清除缓存}else if(WirelessStatus.sendDataFlag == 1 && WirelessStatus.wirelessInitFlag == 1) {WirelessStatus.sendDataFlag = 0;		//清除发送数据标志位Wireless_Publish_Data(2, floatIdentifiers, floatValues, 0, stringIdentifiers, stringValues);	//上报数据}
}

这里代码是放在主函数的while循环中一直执行

在这个代码中可以看到,上报数据和接收数据是互斥的,接收数据处理优先级比发送大,这里这样互斥处理的原因是:如果在wifi模块正在接收OneNET下发的数据时,又正好发送数据,无线模块接收的数据通过串口发送到STM32中,数据中可能会连带两种状态的数据(1:OneNET下发的数据,2:STM32上报数据时发送的AT指令的应答)这样解析接收的数据时出错率会大大提升

上报数据的核心代码:

这里代码逻辑不清楚可以参考下方两个文章:

这两个文章中有说明需要发送的数据json格式和数据类型

物联网实践教程:微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——ESP8266/01s AT指令连接OneNET MQTT篇

OneNET官方文档:MQTT协议接入 最佳实践

手动拼装json格式数据

/**  * @简要  无线模块上报数据	* @参数  floatCount:	需要发送的float数据个数* @参数  *floatIdentifiers[]:float标识符数组* @参数  floatValues[]:float数据数组* @参数  stringCount:需要发送的string数据个数* @参数  *stringIdentifiers[]:string标识符数组* @参数  const char *stringValues[]:string数据数组* @注意事项	这里需要二重转义:第一次转义字符是软件上字符串要求格式,第二次是因为AT指令发送字符串,需要再转义一次,故不方便使用json组装数据* @返回值 无  */void Wireless_Publish_Data(unsigned char floatCount, const char *floatIdentifiers[], float floatValues[], unsigned char stringCount, const char *stringIdentifiers[], const char *stringValues[]) {  // 初始化缓冲区位置  size_t bufferPos = 0;  const uint8_t BUFFER_SIZE = 255;char globalBuffer[BUFFER_SIZE];// 拼接主题和JSON开头  bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos, "%s,\"{\\\"id\\\":\\\"123\\\"\\,\\\"params\\\":{", PUBTOPIC);  // 处理浮点数数据  for (unsigned char i = 0; i < floatCount; ++i) {  bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos,  "\\\"%s\\\":{\\\"value\\\":%.2f\\}%s",  floatIdentifiers[i], floatValues[i],  (i < floatCount - 1 || stringCount > 0) ? "\\," : "");  }  // 处理字符串数据  for (unsigned char i = 0; i < stringCount; ++i) {  bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos,  "\\\"%s\\\":{\\\"value\\\":\\\"%s\\\"\\}%s",  stringIdentifiers[i], stringValues[i],  (i < stringCount - 1) ? "\\," : "");  }  // 拼接JSON结尾  bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos, "}}\",0,0\r\n");  // 确保我们没有超出缓冲区  if (bufferPos >= BUFFER_SIZE) {  // 处理错误,例如通过日志记录  return;  }  Wireless_Send_Command(globalBuffer,"OK",1);}

使用keil.Jansson库组装

需要注意使用jansson需要调大启动文件中的堆空间大小(方法可参考下方链接中)

这里放个keil.Jansson使用方法:

STM32在Keil5中利用Jansson库处理和组装JSON数据【详细版】

	/**  * @简要  无线模块上报数据	* @参数  floatCount:	需要发送的float数据个数* @参数  *floatIdentifiers[]:float标识符数组* @参数  floatValues[]:float数据数组* @参数  stringCount:需要发送的string数据个数* @参数  *stringIdentifiers[]:string标识符数组* @参数  const char *stringValues[]:string数据数组* @注意事项	这里使用json组装,需要动态分配大量的内容,需要将堆大小设置最小0x600* @返回值 无  */void Wireless_Publish_Data(unsigned char floatCount, const char *floatIdentifiers[], float floatValues[], unsigned char stringCount, const char *stringIdentifiers[], const char *stringValues[]) {  json_t *root, *id_obj, *version_obj, *params_obj;  char *json_str;  size_t json_str_len;  // 创建JSON对象  root = json_object();  id_obj = json_string("123");  version_obj = json_string("1.0");  params_obj = json_object();  // 添加id和version到root对象  json_object_set_new(root, "id", id_obj);  json_object_set_new(root, "version", version_obj);  // 添加浮点数数据到params对象  for (unsigned char i = 0; i < floatCount; ++i) {  json_t *float_val_obj = json_object();  json_object_set_new(float_val_obj, "value", json_real(floatValues[i]));  json_object_set_new(params_obj, floatIdentifiers[i], float_val_obj);  }  // 添加字符串数据到params对象  for (unsigned char i = 0; i < stringCount; ++i) {  json_t *string_val_obj = json_object();  json_object_set_new(string_val_obj, "value", json_string(stringValues[i]));  json_object_set_new(params_obj, stringIdentifiers[i], string_val_obj);  }  // 将params对象添加到root对象  json_object_set_new(root, "params", params_obj);  // 将JSON对象转换为字符串  json_str = json_dumps(root, JSON_INDENT(0) | JSON_REAL_PRECISION(4));  json_str_len = strlen(json_str);  // printf("json_str = %s\r\n",json_str);// 为MQTT发布准备最终的消息字符串  char globalBuffer[256];  // 确保缓冲区足够大  snprintf(globalBuffer, sizeof(globalBuffer), "%s,%d,0,0\r\n", ONENET_MQTT_PUB_MQTTPUBRAW, json_str_len + 2);  //添加\r\n的长度// 发送命令  Wireless_Send_Command(globalBuffer, "OK", 1);  strcat(json_str, "\r\n");printf("%s%s",globalBuffer, json_str);Wireless_Send_Command(json_str, "+MQTTPUB:OK", 1);  // 释放JSON对象  json_decref(root);  free(json_str);  // 注意:json_dumps分配的内存需要手动释放  }  

该函数使用示例:

Wireless_Publish_Data(2, floatIdentifiers, floatValues, 0, stringIdentifiers, stringValues);	//代表发送两个浮点数据,0个字符串数据

3.接收OneNET下发的指令数据

这里下发数据部分可以查看该文章:

物联网实践教程:微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——ESP8266/01s AT指令连接OneNET MQTT篇

在OneNET的API调试:物模型设置:设备属性设置,填入对应的json格式数据和参数
如果上文宏定义中的命令标识符是什么,那么OneNET API调试中就应该填写对应的

这是串口接收函数

	if (huart->Instance == USART2){WirelessRx.RxDataCnt = RX_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);if(WirelessRx.RxBuffer[WirelessRx.RxDataCnt - 2] == '\r' && WirelessRx.RxBuffer[WirelessRx.RxDataCnt - 1] == '\n'){WirelessStatus.receiveDataFlag = 1;if(WirelessStatus.wirelessInitFlag == 1) printf("Receive\r\n");//HAL_UART_Transmit_DMA(&huart1, WirelessRx.RxBuffer, WirelessRx.RxDataCnt);}}

这里可以看出,如果wifi模块接收到OneNET下发的数据后,接收标志位会被置1,这里不影响无线模块函数初始化

这里是整个无线模块的运行函数

/**  * @简要  无线模块运行函数	* @参数  无* @注意事项	一直轮询,可以设置为定时触发,但是回应下发的数据会变慢* @返回值 无  */
void Wireless_Loop(void)
{if(WirelessStatus.receiveDataFlag == 1){WirelessStatus.receiveDataFlag = 0;	//清除接收数据标志位Wireless_Receive_Command_Respond((char *)WirelessRx.RxBuffer);		//接收到指令后进行回应操作Wireless_Buffer_Clear();     //执行完接收数据的所有操作后再清除缓存}else if(WirelessStatus.sendDataFlag == 1 && WirelessStatus.wirelessInitFlag == 1) {WirelessStatus.sendDataFlag = 0;		//清除发送数据标志位Wireless_Publish_Data(2, floatIdentifiers, floatValues, 0, stringIdentifiers, stringValues);	//上报数据}
}​

这里代码是放在主函数的while循环中一直执行

可以看出如果接受标志位被置1后,会执行这个函数

/**  * @简要  无线模块接收到下发的数据后进行回应	* @参数  str:无线模块接收到的数据* @注意事项	这里判断下发的指令中是否包含关键字: "+MQTTSUBRECV"里面的printf可以打印接收到的数据和转换后的数据* @返回值 无  */
void Wireless_Receive_Command_Respond(const char * str)
{char *jsonData;char id[5] = {0};int command = 0;if(strstr(str, "+MQTTSUBRECV") != NULL)			//利用AT指令的特性去判断接受的内容是否包含关键字以验证数据来源{//printf("str = %s",str);						//【调试】jsonData = strchr((const char*)str, '{');		//提取json格式数据,通常是以 “{”开始    以“}”结尾//printf("jsonData = %s",jsonData);				//【调试】Remove_Trailing_Crlf(jsonData);					//这里需要去除末尾的换行回车符Wireless_Extract_Receive_Command(jsonData, id, &command);	//这里使用keil.Jansson去提取下发数据的id和commandWireless_Receive_Ack_CloudPlatform((char *)str, id);		//这里回应OneNET,表示接收到了数据Wireless_Receive_Command_Control(command);		//这里对接收到的command执行对应的操作}
}

这个函数会先判断数据来源,再利用Keil.Jansson库解析数据,这个库的使用方法:

STM32在Keil5中利用Jansson库处理和组装JSON数据【详细版】

这个函数就是通过OneNET下发的数据去执行对应的操作


/**  * @简要  执行无线模块接收到下发指令对应的操作	* @参数  cmdValue:提取后的指令数据* @注意事项	这里的指令可以更改为比int更大类型,需要在提取Wireless_Extract_Receive_Command函数中修改提取的数据大小* @返回值 无  */
void Wireless_Receive_Command_Control(int cmdValue)
{//1000 1 0 :1000灯 2 代表2号灯 0代表灭 if(cmdValue == 10021)  	 HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin,GPIO_PIN_SET);else if(cmdValue == 10020)  HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin,GPIO_PIN_RESET); if(cmdValue == 10031)  	 HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin,GPIO_PIN_SET);else if(cmdValue == 10030)  HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin,GPIO_PIN_RESET); 
}

代码下载

地址:

补充

如果后续有什么问题,会在这里说明

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

node.js npm 安装和安装create-next-app

1、官网下载windows版本NODE.JS https://nodejs.org/dist/v20.17.0/node-v20.17.0-x64.msi 2、安装后增加两个文件夹目录node_global、node_cache npm config set prefix "C:\Program Files\nodejs\node_global" npm config set prefix "C:\Program Files\nod…

828华为云征文 | 云服务器Flexus X实例,Docker集成搭建超级马里奥

828华为云征文 | 云服务器Flexus X实例&#xff0c;Docker集成搭建超级马里奥 华为云端口放行 服务器放行对应端口5000 Docker安装并配置镜像加速 1、购买华为云 Flexus X 实例 Flexus云服务器X实例-华为云 (huaweicloud.com) 2、docker安装 yum install -y docker-ce3、验证 …

网络编程——TCP网络通信

通信步骤&#xff1a; 1、连接 2、传输数据 3、关闭连接服务端的创建流程&#xff1a; 1、创建服务端socket对象 socket_family:网络地址类型AF_INET--代表的是ipv4地址类型 socket_type:套接字类型SOCK_STREAM--代表的是tcp套接字SOCK_DGRAM--代表的是udp套接字 2、绑定自己的…

企业如何利用短视频平台做口碑塑造和品牌营销?

抖音和小红书作为短视频平台的代表&#xff0c;吸引了大量的用户和品牌。如何利用抖音、小红书等短视频平台进行品牌塑造和口碑营销呢&#xff1f;小马识途营销顾问分析&#xff0c;短视频平台的用户以年轻人为主&#xff0c;他们具有高度的社交性和消费意愿。短视频平台提供了…

使用Java基于GeoTools读取Shapefile矢量数据属性信息-以某市POI数据为例

前言 在之前的博客中&#xff0c;我们讲过在GDAL中如何读取空间数据的属性和数据信息&#xff0c;也简单的讲过如何在GeoTools中读取Shapefile文件的属性信息和数据信息。对于空间矢量数据库&#xff0c;就像我们传统的二维数据库的表字段和表数据的关系&#xff0c;在研究表数…

[大语言模型-论文精读] ACL2024-长尾知识在检索增强型大型语言模型中的作用

ACL2024-长尾知识在检索增强型大型语言模型中的作用 On the Role of Long-tail Knowledge in Retrieval Augmented Large Language Models Authors: Dongyang Li, Junbing Yan, Taolin Zhang, Chengyu Wang, Xiaofeng He, Longtao Huang, Hui Xue, Jun Huang 1.概览 问题解决&…

引领开放式耳机革命,南卡新品再次霸榜音质和舒适度TOP1!

​随着科技的不断进步&#xff0c;消费者对于耳机的需求已经不仅仅局限于基本的听觉体验&#xff0c;而是追求更加舒适、个性化的聆听感受。南卡科技推出的Ultra夹耳开放式蓝牙耳机&#xff0c;以其创新的设计和卓越的音质&#xff0c;为用户带来了全新的听觉盛宴。 南卡科技&a…

【Linux探索学习】第一弹——Linux的基本指令(上)——开启Linux学习第一篇

前言&#xff1a; 在进入Linux学习之前&#xff0c;我们首先要先做好以下两点&#xff1a;1、已经基本掌握C语言或C&#xff0c;2、已经配置好了Linux的环境&#xff0c;做完以上两点后我们就开始Linux的学习&#xff0c;今天我们首先要学习的就是Linux中最基础的操作&#xff…

Python自动化测试之unittest框架

一、什么是框架 框架是由大佬开发或者专业的研发团队研发的技术骨架&#xff0c;框架是一个半成品&#xff0c;框架是对常用的功能&#xff0c;基础的代码进行封装的一个工具&#xff0c;这个工具对外提供了一些API&#xff0c;其他的开发者只需要调用框架的接口即可&#xff…

C++语言桌面应用开发GTK3 Gtkmm3 Glade

文章目录 Gikmm 简介安装 Gtkmm安装 GTK安装 Gladedemo.glade 文件完整示例 demo.cpp编译运行GTK 主题推荐主题 Gikmm 简介 Gtkmm 是一个用于创建图形用户界面&#xff08;GUI&#xff09;的 C 库&#xff0c;它是基于流行的 GTK 库的。GTK 是一个跨平台的 GUI 工具包&#xff…

HTML5基本概念及其基本框架

HTML是超文本标记语言&#xff08;英语&#xff1a;HyperText Markup Language&#xff0c;简称&#xff1a;HTML&#xff09;是一种用来结构化 Web 网页及其内容的标记语言。网页内容可以是&#xff1a;一组段落、一个重点信息列表、也可以含有图片和数据表。 H5的DOCTYPE的声…

新手操作指引:快速上手腾讯混元大模型

引言 腾讯混元大模型是一款功能强大的AI工具&#xff0c;适用于文本生成、图像创作和视频生成等多种应用场景。对于新手用户&#xff0c;快速上手并充分利用这一工具可能会有些挑战。本文将提供详细的新手操作指引&#xff0c;帮助您轻松开始使用腾讯混元大模型。 步骤一&…

PyTorch模型转ONNX量化模型

你是否发现模型太大&#xff0c;无法部署在你想要的云服务上&#xff1f;或者你是否发现 TensorFlow 和 PyTorch 等框架对于你的云服务来说太臃肿了&#xff1f;ONNX Runtime 可能是你的救星。 如果你的模型在 PyTorch 中&#xff0c;你可以轻松地在 Python 中将其转换为 ONNX…

智能感知,主动防御:移动云态势感知为政企安全护航

数字化时代&#xff0c;网络安全已成为企业持续运营和发展的重要基石。随着业务扩展&#xff0c;企业资产的数量急剧增加&#xff0c;且分布日益分散&#xff0c;如何全面、准确地掌握和管理资产成为众多政企单位的难题。同时&#xff0c;传统安全手段又难以有效应对新型、隐蔽…

你的提交信息还在拖后腿?看这里,提升代码质量的绝招!

文章目录 前言一、什么是约定式提交&#xff1f;二、创建新仓库三、将代码推送到远程仓库的步骤1.检查当前远程仓库2.添加代码到暂存区3. 进行约定式提交4. 推送代码到远程仓库5. 完成推送 总结 前言 在当今软件开发领域&#xff0c;Git已经成为最广泛使用的版本控制系统之一。…

二阶滤波算法总结(对RC滤波算法整理的部分修正和完善)

文章目录 1、一阶低通滤波2、一阶高通滤波3、二阶低通滤波器3.1 二阶RC低通滤波器的连续域数学模型3.2 二阶RC低通滤波器的算法推导3.3 matlab仿真 4、二阶高通滤波器4.1 二阶RC高通滤波器的连续域数学模型4.2 二阶RC高通滤波器的算法推导4.3 matlab仿真 5、陷波滤波6、带通滤波…

白杨SEO:从小红书、抖音图文再到小绿书,为什么现在制作图文内容搞SEO搜索精准流量更容易?

前言&#xff1a;为什么想到写这个&#xff1f;上周参加了一个杭州公司游学&#xff0c;发现大家现在做SEO精准流量都在用图文方式来搞了&#xff0c;还有做小绿书也越来越多了&#xff0c;所以分享给大家&#xff0c;看完对大家有一些启发。 文章大纲&#xff1a; 1、图文是什…

2024年AI技术爆发的元年,用对工具,让你副业比主业赚得多!

大家好&#xff0c;我是强哥 文字的力量不容小觑&#xff0c;或许你没有多好的文笔&#xff0c;或许你已经很久没有拿笔写字了&#xff0c;但是没关系&#xff0c;我们有工具&#xff01; AI时代的到来&#xff0c;不会用工具&#xff0c;那你可就OUT了 如果你觉得文字不能赚…

产业报告丨2024中国AI大模型场景探索及产业应用调研报告(附下载)

前言 AI大模型是指在机器学习和深度学习领域中&#xff0c;采用大规模参数&#xff08;至少在一亿个参数以上&#xff09;的神经网络模型&#xff0c;AI大模型在训练过程中需要依赖大量的算力和高质量的数据资源。2024年&#xff0c;AI大模型的行业应用与技术发展正有效提升千…

2024年 AI大模型我该买一张什么卡?

有钱啥也不用说&#xff0c;买张最贵的就是了。对囊中羞涩的我还说&#xff0c;我该买张什么样的显卡呢&#xff1f; 我的旧显卡RTX1060 6G&#xff0c;满负荷消耗功率110多瓦&#xff0c;几乎达到设计最大TDP&#xff0c;周日时拿了朋友的RTX3060Ti 8G&#xff0c;发现是锁算…