STM32HAL库+ESP8266+cJSON+微信小程序_连接华为云物联网平台

news2024/9/22 21:30:25

STM32HAL库+ESP8266+cJSON+微信小程序_连接华为云物联网平台

实验使用资源:正点原子F407

USART1:PA9P、A10(串口打印调试)

USART3:PB10、PB11(WiFi模块)

DHT11:PG9(采集数据、上报)

LED0、1:PF9、PF10(根据收到的命令,控制亮灭)

显示屏(可有可无)

0 前置内容准备

以下内容请参考之前写过的博客

  • HAL库创建工程【STM32CubeMX安装_stm32cubemx下载-CSDN博客】
  • ESP8266固件烧录【ESP8266烧录AT指令】

1 华为云物联网平台创建产品

1.1新建产品

  1. 在华为云设备接入IoTDA平台,点击左上角的【创建产品】,参考下图填写产品信息。

在这里插入图片描述

  1. 创建完成后进入创建的产品,进行产品模型设置。首先先创建一个服务,服务ID自己根据实际写。
  2. 点击【新增属性】,根据实际需求,添加需要交互的数据及其类型和访问方式。

  1. 点击【添加命令】,进行指令的设置,【下发参数】就是下发指令控制设备,【响应参数】就是读取设备上传信息。

在这里插入图片描述

1.2 新建设备

在【所有设备】界面,点击的【注册设备】添加设备

在这里插入图片描述

1.3 获取MQTT三元素

在新创建的设备中,点击查看MQTT连接参数,即可获取三元素

在这里插入图片描述

1.4 获取订阅Topic

在之前创建的产品中,Topic管理里面包含我们需要订阅的Topic

在这里插入图片描述

2 usart模块

  1. 将【stm32f1xx_it.c】里面的void USART1_IRQHandler(void)void USART3_IRQHandler(void)函数注释掉

在这里插入图片描述

  1. 将下面的代码粘贴到【usart.c】中的最下面的/* USER CODE BEGIN 1 *//* USER CODE END 1 */之间
/**
 * @brief       ATK-MW8266D UART printf
 * @param       fmt: 待打印的数据
 * @retval      无
 */
void atk_mw8266d_uart_printf(char *fmt, ...)
{
    va_list ap;
    uint16_t len;
    
    va_start(ap, fmt);
    vsprintf((char *)g_uart_tx_buf, fmt, ap);
    va_end(ap);
    
    len = strlen((const char *)g_uart_tx_buf);
    HAL_UART_Transmit(&huart3, g_uart_tx_buf, len, HAL_MAX_DELAY);
}

/**
 * @brief       ATK-MW8266D UART重新开始接收数据
 * @param       无
 * @retval      无
 */
void atk_mw8266d_uart_rx_restart(void)
{
    g_uart_rx_frame.sta.len     = 0;
    g_uart_rx_frame.sta.finsh   = 0;
}

/**
 * @brief       获取ATK-MW8266D UART接收到的一帧数据
 * @param       无
 * @retval      NULL: 未接收到一帧数据
 *              其他: 接收到的一帧数据
 */
uint8_t *atk_mw8266d_uart_rx_get_frame(void)
{
    if (g_uart_rx_frame.sta.finsh == 1)
    {
        g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = '\0';
        return g_uart_rx_frame.buf;
    }
    else
    {
        return NULL;
    }
}

/**
 * @brief       获取ATK-MW8266D UART接收到的一帧数据的长度
 * @param       无
 * @retval      0   : 未接收到一帧数据
 *              其他: 接收到的一帧数据的长度
 */
uint16_t atk_mw8266d_uart_rx_get_frame_len(void)
{
    if (g_uart_rx_frame.sta.finsh == 1)
    {
        return g_uart_rx_frame.sta.len;
    }
    else
    {
        return 0;
    }
}


void USART1_IRQHandler(void)
{
#if SYS_SUPPORT_OS                                                   /* 使用OS */
    OSIntEnter();
#endif
    HAL_UART_IRQHandler(&huart1);                               /* 调用HAL库中断处理公用函数 */

    while (HAL_UART_Receive_IT(&huart1, (uint8_t *)g_rx_buffer, RXBUFFERSIZE) != HAL_OK)     /* 重新开启中断并接收数据 */
    {
        /* 如果出错会卡死在这里 */
    }

#if SYS_SUPPORT_OS                                                   /* 使用OS */
    OSIntExit();
#endif
}

void USART3_IRQHandler(void)
{
  /* USER CODE BEGIN USART3_IRQn 0 */

  /* USER CODE END USART3_IRQn 0 */
  HAL_UART_IRQHandler(&huart3);
  /* USER CODE BEGIN USART3_IRQn 1 */
    uint8_t tmp;
    
    if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_ORE) != RESET)        /* UART接收过载错误中断 */
    {
        __HAL_UART_CLEAR_OREFLAG(&huart3);                           /* 清除接收过载错误中断标志 */
        (void)huart3.Instance->SR;                                   /* 先读SR寄存器,再读DR寄存器 */
        (void)huart3.Instance->DR;
    }
    
    if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_RXNE) != RESET)       /* UART接收中断 */
    {
        HAL_UART_Receive(&huart3, &tmp, 1, HAL_MAX_DELAY);           /* UART接收数据 */
        if (g_uart_rx_frame.sta.len < (256 - 1))   /* 判断UART接收缓冲是否溢出
                                                                             * 留出一位给结束符'\0'
                                                                             */
        {
            g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = tmp;             /* 将接收到的数据写入缓冲 */
            g_uart_rx_frame.sta.len++;                                      /* 更新接收到的数据长度 */
        }
        else                                                                /* UART接收缓冲溢出 */
        {
            g_uart_rx_frame.sta.len = 0;                                    /* 覆盖之前收到的数据 */
            g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = tmp;             /* 将接收到的数据写入缓冲 */
            g_uart_rx_frame.sta.len++;                                      /* 更新接收到的数据长度 */
        }
    }
    
    if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE) != RESET)       /* UART总线空闲中断 */
    {
        g_uart_rx_frame.sta.finsh = 1;                                      /* 标记帧接收完成 */
        
        __HAL_UART_CLEAR_IDLEFLAG(&huart3);                          /* 清除UART总线空闲中断 */
    }
  /* USER CODE END USART3_IRQn 1 */
}
  1. 在【usart.c】上面的的/* USER CODE BEGIN 0 *//* USER CODE END 0 */之间加入下面的代码
#include <stdarg.h>
#include <stdio.h>
#include <string.h>


#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 

}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (uint8_t) ch;      
	return ch;
}
#endif 

uint8_t g_rx_buffer[RXBUFFERSIZE];  /* HAL库使用的串口接收缓冲 */
  1. 在【usart.c】中的void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)函数中调整中断优先级,WiFi的usart3的高于串口的,同时添加usar使能

在这里插入图片描述

  1. 在【usart.h】中的/* USER CODE BEGIN Private defines *//* USER CODE END Private defines */之间加入下面的代码
static struct
{
    uint8_t buf[256];              /* 帧接收缓冲 */
    struct
    {
        uint16_t len    : 15;                               /* 帧接收长度,sta[14:0] */
        uint16_t finsh  : 1;                                /* 帧接收完成标志,sta[15] */
    } sta;                                                  /* 帧状态信息 */
} g_uart_rx_frame = {0};                                    /* ATK-MW8266D UART接收帧缓冲信息结构体 */
static uint8_t g_uart_tx_buf[1024]; /* ATK-MW8266D UART发送缓冲 */
#define RXBUFFERSIZE   1                        /* 缓存大小 */
  1. 在【usart.h】中的/* USER CODE BEGIN Prototypes *//* USER CODE END Prototypes */之间加入下面的代码
void atk_mw8266d_uart_printf(char *fmt, ...);       /* ATK-MW8266D UART printf */
void atk_mw8266d_uart_rx_restart(void);             /* ATK-MW8266D UART重新开始接收数据 */
uint8_t *atk_mw8266d_uart_rx_get_frame(void);       /* 获取ATK-MW8266D UART接收到的一帧数据 */
uint16_t atk_mw8266d_uart_rx_get_frame_len(void);   /* 获取ATK-MW8266D UART接收到的一帧数据的长度 */

3 WiFi模块

将提前编写好的esp8266.c/.h文件分别加入Src和Inc文件夹,然后再在keil里将esp8266.c文件加入工程。

4 CJSON模块移植

移植过程查看之前写的另一篇博客【CJSON模块】

5 WIFI连接华为云

  1. 创建wifi_HW.c/.h文件。
  2. 在wifi_HW.h中宏定义连接华为云相关参数。
// Wi-Fi用户名和密码
#define WIFI_User 						 "***"  // Wi-Fi网络名称
#define WIFI_Pass   					 "***"  // Wi-Fi网络密码

// ESP8266设备在华为云IoT平台的认证信息
#define ESP8266_UserName 				 "***"  // 用户名,通常为设备ID
#define ESP8266_PassWord				 "***"  // 密码或密钥
#define ESP8266_ClientID				 "***"  // MQTT客户端ID
#define ESP8266_Domain_Name				 "***"  // 华为云IoT平台的域名
#define ESP8266_Port 					 1883  // MQTT服务端口
#define ESP8266_Reconnect				 1  // 是否允许设备在断开连接后尝试重新连接

// 华为云MQTT服务相关设置
#define HUAWEI_MQTT_ServiceID			 "***"  // 服务ID
#define HUAWEI_MQTT_DeviceID	         "***"  // 设备ID
#define HUAWEI_MQTT_commands             "$oc/devices/***/sys/commands/#"  // 命令订阅主题
#define HUAWEI_MQTT_commands_response    "$oc/devices/***/sys/commands/response/request_id="  // 命令响应主题
#define HUAWEI_MQTT_report               "$oc/devices/***/sys/properties/report"  // 属性上报主题
  1. 编写wifi_HW.c文件,调用esp8266.c中编写的函数连接华为云,然后将该函数名加入wifi_HW.h中。
void wifi_init(void){
	uint8_t ret = 0;
	ret  = ESP8266_Reset();
	printf("1: %d\r\n",ret);
	ret = ESP8266_ATE(0);
	printf("2: %d\r\n",ret);
	ret = ESP8066_Mode(1);
	printf("3: %d\r\n",ret);
	ret = ESP8266_WiFi(WIFI_User, WIFI_Pass);
	printf("4: %d\r\n",ret);
	ret = ESP8266_MQTTUSERCFG(ESP8266_UserName, ESP8266_PassWord);
	printf("5: %d\r\n",ret);
	ret = ESP8266_MQTTCLIENTID(ESP8266_ClientID);
	printf("6: %d\r\n",ret);
	ret =  ESP8266_MQTTCONN(ESP8266_Domain_Name,ESP8266_Port,ESP8266_Reconnect);
	printf("7: %d\r\n",ret);
	ret =  ESP8266_MQTTSUB(HUAWEI_MQTT_commands);
	printf("8: %d\r\n",ret);
}

6 数据交互

6.1 设备属性上报

参考华为云官方给出的MQTT属性上报样例,在wifi_HW.c中编写report_Json函数,使用cJSON模块封装传感器数据,然后将其发送到云平台。

在这里插入图片描述

可以看出该消息的JSON格式:

(1) 在根对象中,有一个名为services的键,其值是一个数组。

(2) 在services数组中,有一个服务对象,它包含两个键:service_id和properties

(3) 在properties对象中,存放的键值对就是在产品中创建的属性。

根据这种格式,在report_Json函数中封装JSON消息

void report_Json(uint8_t temperature, uint8_t humidity,uint8_t adcx){
    uint8_t cmd[1024]; // 用于存储构建的AT命令
    char *str = NULL; // 指向JSON字符串的指针
    int i = 0; // 循环迭代变量
    uint8_t params_buf[1024]; // 用于存储处理过的JSON字符串
    uint16_t move_num = 0; // 用于记录字符串处理过程中的移动次数
    
    cJSON *json = cJSON_CreateObject(); // 创建一个空的JSON对象
    cJSON *properties_cjson = cJSON_CreateObject(); // 创建一个空的子JSON对象,用于存储属性
    cJSON *service = cJSON_CreateObject(); // 创建一个空的子JSON对象,用于存储服务信息
    cJSON *services_array = cJSON_CreateArray(); // 创建一个空的JSON数组,用于存储服务数组
	
	// 向属性对象中添加温度、湿度和光照强度数据
    cJSON_AddNumberToObject(properties_cjson, "temperature", temperature);
    cJSON_AddNumberToObject(properties_cjson, "humidity", humidity);
	cJSON_AddNumberToObject(properties_cjson, "light", adcx);
    // 向服务对象中添加服务ID和属性
	cJSON_AddStringToObject(service, "service_id", "yun");
    cJSON_AddItemToObject(service, "properties", properties_cjson);
	cJSON_AddItemToObject(json, "services", services_array);
	 // 将服务数组添加到JSON对象中
	cJSON_AddItemToArray(services_array, service);
    
    // 将JSON对象转换为无格式的字符串
    str = cJSON_PrintUnformatted(json);
    
    // 打印JSON格式的字符串
    //printf("json格式 = %s\r\n", str);
    
    // 为MQTT发布添加转义字符
    for(i = 0; *str != '\0'; i++){
        params_buf[i] = *str;
        // 如果下一个字符是引号或逗号,添加转义字符
        if(*(str + 1) == '"' || *(str + 1) == ','){
            params_buf[++i] = '\\';
        }
        str++;
        move_num++;
    }
    str = str - move_num; // 回退指针到JSON字符串的开始
    
    // 打印处理过的JSON字符串
    //printf("params_buf = %s\r\n", params_buf);
    
    // 构建AT命令
    sprintf((char *)cmd,"AT+MQTTPUB=0,\""HUAWEI_MQTT_report"\",\"%s\",0,0\r\n",params_buf);
    
    // 打印即将发送的AT命令
    //printf("开始发送数据:%s", cmd);

    // 发送AT命令并通过ESP8266模块
    ESP8266_Sent_AT(cmd, "OK", 500);
    
    // 清理JSON对象占用的内存
    cJSON_Delete(json);
    
    // 如果分配了额外的字符串空间,释放它
    if(str != NULL){
        free(str);
        str = NULL;
        //printf("释放str空间成功\r\n");
    }
}

6.2 云端命令下发

参考华为云官方给出的MQTT下行请求样例,在wifi_HW.c中编写rcv_json函数,解析云平台下发的JSON消息字符串,如果成功接收后,需要给云平台返回接收成功的消息命令。
在这里插入图片描述

从给出的样例中可以看出,下发的指令在paras键所对应的JSON值中,只需分析其中的键的名字,然后读取相匹配的键的值,即可获取下发的命令。

参考下图响应参数的格式,可知响应参数只需发送订阅的topic和下发命令中的request_id的值即可,后面的JSON消息体均可省略。

在这里插入图片描述

void rcv_json(void){
    uint8_t *ret = NULL; // 用于存储接收到的数据帧
    cJSON *cjson = NULL; // 用于存储解析后的JSON对象
    char topic_buff[1024]; // 用于存储MQTT主题
    int num; // 用于存储接收数据的数量
    char recv_buffer[1024]; // 用于存储接收到的JSON数据
	char request_id[37];
	char device_id[256];
	uint8_t cmd[1024]; // 用于存储构建的AT命令
    
    ret = atk_mw8266d_uart_rx_get_frame(); // 获取UART接收到的数据帧
    atk_mw8266d_uart_rx_restart(); // 重启UART接收
	
    char *ptr_recv = strstr((const char *)ret,"+MQTTSUBRECV"); // 检查是否包含MQTT订阅数据标志
    
    if(ptr_recv!=NULL){ // 如果是MQTT订阅数据
        memset(device_id,0,sizeof(device_id)); 
		memset(request_id,0,sizeof(request_id)); 
        
		sscanf((char *)ret, "+MQTTSUBRECV:0,\"$oc/devices/%255[^/]/sys/commands/request_id=%36s\",%d,%255s", device_id, request_id, &num, recv_buffer);
		
		//printf("判断DeviceID:  %s\r\n",device_id);
		
		// printf("接收数据成功,开始解析  %s\r\n",recv_buffer);
		//判断DeviceID
        if(strstr(device_id,HUAWEI_MQTT_DeviceID)) {
            // printf("接收数据成功,开始解析  %s\r\n",recv_buffer);
            cjson = cJSON_Parse(recv_buffer); // 解析JSON数据
        }
        
        if(cjson==NULL) // 如果JSON解析失败
            printf("cjson 解析错误\r\n"); // 打印错误信息
        else{
            cJSON *json_data = cJSON_GetObjectItem(cjson,"paras"); // 获取JSON对象中的paras项
            if(json_data==NULL){ 
                printf("cjson  没有数据\r\n"); // 打印错误信息
                return;
            }
            else{
                // printf("cjson 内存大小为 = %d\r\n",sizeof(cjson)); // 打印JSON对象的内存大小
                // 解析数据
                if(cJSON_GetObjectItem(json_data,"led_flag")!=NULL) 
                {
                    LED_Switch = cJSON_GetObjectItem(json_data,"led_flag")->valueint; 
                    printf("csjon解析成功 LED_Switch = %d\r\n",LED_Switch); 

                }
                if(cJSON_GetObjectItem(json_data,"temp_flag")!=NULL) 
                {
                    temp_LED_Switch = cJSON_GetObjectItem(json_data,"temp_flag")->valueint; 
                    printf("csjon解析成功 temp_LED_Switch = %d\r\n",temp_LED_Switch);
                }
				char full_topic[256];    
				snprintf(full_topic, sizeof(full_topic), "%s%s", HUAWEI_MQTT_commands_response, request_id);
				sprintf((char *)cmd, "AT+MQTTPUB=0,\"%s\",\"\",0,0\r\n", full_topic);
				ESP8266_Sent_AT(cmd, "OK", 500);
            }
            cJSON_Delete(cjson); // 删除JSON对象,释放内存
            //cJSON_Delete(json_data);
        }
    }
}

7 微信小程序连接华为云

7.1 开发方式

  1. 使用微信开发工具创建微信小程序项目
  2. 微信小程序调用应用侧API,获取云平台数据
  3. 将获得的数据进行显示

7.2 微信小程序创建

【略…】

7.3 创建IAM账户(云平台)

  1. 统一身份认证(Identity and Access Management,简称IAM)是华为云提供权限管理的基础服务,可以安全地控制云服务和资源的访问权限。应用侧需要通过IAM服务鉴权,获取token。因此在开发之前需要先创建IAM用户。
  2. 在【统一身份认证服务】页面,点击【创建用户】,参考下图创建账号,里面填写的自定义密码需要记住,然后将其加入【admin】用户组。

在这里插入图片描述

  1. 创建成功后,退出登录华为云,使用IAM账号登录华为云,验证是否成功。

7.3 获取Token(小程序)

  1. 参考华为云提供的请求示例,发送获取Token的http请求消息

在这里插入图片描述

  1. 华为云认证通过后向应用服务器返回鉴权令牌X-Subject-Token,接口返回的响应消息头中X-Subject-Token就是需要获取的用户Token。根据上述示例,在微信开发工具中,使用wx.request发送请求,然后分析响应消息头,获取Token。
gettoken(){
    var that=this;   
    wx.request({
        url: 'https://iam.cn-north-4.myhuaweicloud.com/v3/auth/tokens',
        data:'{"auth": { "identity": {"methods": ["password"],"password": {"user": {"name": "***","password": "***","domain": {"name": "***"}}}},"scope": {"project": {"name": "cn-north-4"}}}}',
        method: 'POST', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
        header: {'content-type': 'application/json' }, // 请求的 header 
        success: function(res){// success
            var token='';
            token=JSON.stringify(res.header['X-Subject-Token']);//解析消息头的token
            token=token.replaceAll("\"", "");
            wx.setStorageSync('token',token);
        },
    });
}

  1. 获取Token后,再调用其他接口时,需要在请求消息头中添加X-Auth-Token,其值为获取到的Token。例如Token值为“ABCDEFJ…”,则调用接口时将“X-Auth-Token: ABCDEFJ…”加到请求消息头即可,如下所示。

在这里插入图片描述

7.4 获取设备影子(小程序)

  1. 参考请求示例,发送请求获取设备影子数据。

在这里插入图片描述

  1. 查看返回的HTTP响应消息,查看所需要的数据所在位置,如下图所示,可以发现我们需要的数据在data里面的shadow[0]中的reported下的properties里面。

在这里插入图片描述

  1. 根据分析的结果,在微信开发工具中编写代码,获取数据。
getinfo(){
    var that = this
    var token = wx.getStorageSync('token');//读缓存中保存的token
      wx.request({
        url: 'https://653a151f50.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/661f377e2ccc1a583881a678/devices/661f377e2ccc1a583881a678_yun_dht11/shadow',
        data:'',
        method: 'GET', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
        header: {'content-type': 'application/json','X-Auth-Token':token },
      success: function (res) {
      that.setData({
        humi:res.data.shadow[0].reported.properties.humidity,
        temp:res.data.shadow[0].reported.properties.temperature,
        light:res.data.shadow[0].reported.properties.light
      })
      }
     });
}

7.5 下发设备命令(小程序)

参考请求示例,在微信开发工具中编写代码下发命令。

在这里插入图片描述

if(that.data.lset > that.data.light){
    wx.request({
      url: 'https://653a151f50.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/661f377e2ccc1a583881a678/devices/661f377e2ccc1a583881a678_yun_dht11/commands',
      data:'{"service_id": "yun","command_name": "led","paras": {"led_flag": true}}',
      method: 'POST', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
      header: {'content-type': 'application/json','X-Auth-Token':token }, //请求的header 
      success: function(res){
          console.log("下发命令成功");//打印完整消息
      }, 
    });
}

8 小程序界面设计

8.1 主页数据显示

在wxml中设置布局,在wxss中设置样式,如下图所示。

在这里插入图片描述

8.2 Echarts数据可视化

ECharts一个使用JavaScript实现的开源可视化库。ECharts提供了常规的折线图、柱状图、散点图、饼图、K线图,用于统计的盒形图,用于地理数据可视化的地图、热力图、线图,用于关系数据可视化的关系图、treemap、旭日图,多维数据可视化的平行坐标,还有用于BI的漏斗图,仪表盘,并且支持图与图之间的混搭。

微信小程序使用Echarts进行可视化的步骤如下所示。

  1. 将Echarts下载到本地,并放入的微信小程序工程文件夹中,如下图所示。

在这里插入图片描述

  1. 在lineset.json中引入ec-canvas组件,如下图所示。

在这里插入图片描述

  1. 参考官网给出的折线图堆叠示例,编写该图像的显示函数function line_set(chart,time_data,hum_data,tem_data,ligh_data)。

  2. 初始化图表,同时调用line_set函数显示。

init_chart: function (time_data,hum_data,tem_data,ligh_data) {        
    this.oneComponent.init((canvas, width, height, dpr) => {
        const chart = echarts.init(canvas, null, {
            width: width,
            height: height,
            devicePixelRatio: dpr 
        });
        line_set(chart,time_data,hum_data,tem_data,ligh_data)
        this.chart = chart;
        return chart;
    });
}
  1. 将获得的影子设备的数据存入数组中,然后将其传入init_chart(date,humi,temp,ligh)中,进行数据实时可视化显示。
temp = that.data.Temperature;
humi = that.data.Humidity;
ligh = that.data.Light;
date = that.data.Time;
temp.push(res.data.shadow[0].reported.properties.temperature);
humi.push(res.data.shadow[0].reported.properties.humidity);
ligh.push(res.data.shadow[0].reported.properties.light);
date.push(formattedTime);
if (temp.length > 7) {
  temp.shift(); // 移除数组的第一个元素
  humi.shift();
  ligh.shift();
  date.shift();
}

32工程源码(CSDN):【免费】STM32HAL库+ESP8266+cJSON+微信小程序-连接华为云物联网平台.zip资源-CSDN文库

微信小程序源码(CSDN):【免费】STM32HAL库+ESP8266+cJSON+微信小程序-连接华为云物联网平台(微信小程序侧)资源-CSDN文库

32工程+微信小程序源码(GitHub):STM32HAL库+ESP8266+cJSON+微信小程序_连接华为云物联网平台

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

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

相关文章

【JavaEE精炼宝库】文件操作(2)——文件内容读写 | IO流

文章目录 一、输入流1.1 InputStream 概述&#xff1a;1.2 read 方法详解&#xff1a;1.3 close 方法&#xff1a;1.4 利用 Scanner 进行读操作&#xff1a;1.5 Reader&#xff1a; 二、输出流2.1 OutputStream 概述&#xff1a;2.2 write 方法详解&#xff1a;2.3 利用 PrintW…

【Sql Server】sql server 2019设置远程访问,外网服务器需要设置好安全组入方向规则

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言1、无法链接…

47、lvs之DR

1、DR模式&#xff1a; 1.1、lvs三种模式&#xff1a; nat 地址转换 DR 直接路由模式 tun 隧道模式 1.2、DR模式的特点&#xff1a; 调度器在整个lvs集群当中是最重要的&#xff0c;在nat模式下&#xff0c;即负载接收请求&#xff0c;同时根据负载均衡的算法转发流量&…

2024 China Joy 前瞻 | 腾讯网易发新作,网易数智携游戏前沿科技、创新产品以及独家礼盒,精彩不断!

今年上半年&#xff0c;CES、MWC和AWE三大国际科技展轮番轰炸&#xff0c;吸引全球科技爱好者的高度关注&#xff0c;无论是新潮的科技产品&#xff0c;还是对人工智能的探索&#xff0c;每一项展出的技术和产品都引起了市场的热议。而到了下半年&#xff0c;一年一度的China J…

springboot商城综合项目自动化系统-计算机毕业设计源码051018

摘 要 目前电商系统商城项目管理极其频繁,迫切地需要自动化测试来代替人工繁琐而又重复的劳动。自动化测试相关的研究已经很多,但多数只是针对某一方面,比如单一接口或者单一页面或者性能等,而缺乏将接口、页面、持续集成系统和缺陷管理系统整合的自动化测试平台。本研究采用混…

ROS——六轴机械臂建模

创建工作空间及目录&#xff08;my_arm&#xff09; 创建launch、urdf文件夹 urdf launch Arm_Model.xacro <?xml version"1.0"?> <robot name"marm" xmlns:xacro"http://www.ros.org/wiki/xacro"><!-- Defining the c…

【Python3】自动化测试_Playwright的安装教程

安装playwright库&#xff1a; pip install playwright 安装内置浏览器&#xff1a; # Install All playwright install# Install chromium playwright install chromium# Install firefox playwright install firefox# Install WebKit playwright install webkit 安装系统…

32.同步FIFO-IP核的调用

&#xff08;1&#xff09;FIFO&#xff08;First In First Out&#xff0c;即先进先出&#xff09;&#xff0c;是一种数据缓冲器&#xff0c;用来实现数据先入先出的读写方式。 &#xff08;2&#xff09;FIFO存储器主要是作为缓存&#xff0c;应用在同步时钟系统和异步时钟系…

深入解析发生 OOM 的三大场景

深入解析 OOM 的三大场景 什么是 OOM&#xff1f;一、堆内存溢出 ( Heap OOM )原因分析解决方案 二、栈内存溢出&#xff08;Stack OOM&#xff09;原因分析解决方案 三、方法区内存溢出&#xff08;Metaspace OOM&#xff09;原因分析解决方案 在Java应用程序开发中&#xff0…

CORDIC Translate

随便记录一下下&#xff1a; Cordic IP核使用说明以及避坑记录-CSDN博客 本次只用到了Translate&#xff0c;记录一下自己遇到的坑坑 实际配置&#xff1a; timescale 1ns / 1nsmodule cordic_tb();reg clk;wire m_axis_dout_tvalid;reg s_axis_cartesian_tvalid 0;wire [31…

三分钟了解什么是消费返利

消费返利作为一种创新的营销手段&#xff0c;不仅增强了消费者的购物体验&#xff0c;还为电商平台及商家带来了更多的用户粘性和复购率。什么是消费返利&#xff0c;其背后的运作机制又是如何的呢&#xff1f; 返利的定义: 返利即是一种消费激励措施&#xff0c;当消费者在指…

网络安全防御 -- 防火墙安全策略用户认证综合实验

实验拓扑&#xff1a; 实验目的&#xff1a; 1、DMZ区内的服务器&#xff0c;办公区仅能在办公时间内(9:00-18:00)可以访问&#xff0c;生产区的设备全天可以访问。 2、生产区不允许访问互联网&#xff0c;办公区和游客区允许访问互联网。 3、办公区设备10.0.2.10不允许访问DM…

ns3-gym入门(一):代码结构和简单例子

ns3-gym真的好难学&#xff0c;网上可以参考的例子也太少了&#xff0c;如果有用这个做路由的麻烦联系我交流一下吧&#xff0c;太痛苦了 一、安装 之前的文章已经提到过了&#xff0c;这里不赘述了 二、运行简单的例子 用两个终端的方式运行感觉更加直观&#xff0c;但是仍然…

白嫖3个域名用cloudeflare托管

这也是看到别的up主分享得到的&#xff0c;我也分享一下给你们。 浏览器打开官网 https://register.us.kg/ 然后注册 根据信息填地址电话可以去另一个网页生成&#xff0c;名字姓名之类的可以填自己喜欢的&#xff0c;注意一下&#xff0c;我尝试了一下谷歌的邮箱&#xff0…

webstorm问题解决:无法识别 @

问题解决tsconfig.json 问题 本地的 vite.config.ts 已经配置 路径 但是&#xff0c;我用webstorm 上识别不了 解决 新增文件tsconfig.json&#xff0c;添加 baseUrl 和 paths 的配置&#xff0c;以告诉 TypeScript 和 WebStorm 如何解析路径别名 tsconfig.json {&quo…

以设备为核心的状态自动采集、人工运维和预测性维护为一体的智能管理系统

中服云设备全生命周期管理系统充分利用物联网、人工智能、机器学习、大数据等新一代技术&#xff0c;实现对企业生产设备从采购、安装、调试、使用、维护、维修、改造、更新直到报废全生命周期的智能化、数字化、可视化的实时管控&#xff0c;支持设备运行状态的自动采集和人工…

SketchUp Pro 2023: 强大的跨平台建模软件

SketchUp Pro 2023 是一款广受建筑设计师、工程师和创意设计师欢迎的专业建模软件&#xff0c;它结合了直观的用户界面和强大的功能&#xff0c;为用户提供了创造、设计和分享他们的想法的理想平台。无论是初学者还是经验丰富的专业人士&#xff0c;SketchUp Pro 2023 都能够满…

JVM是如何管理内存的?图文详解GC垃圾回收算法

前言&#xff1a;在C/C中对于变量的内存空间一般都是由程序员手动进行管理的&#xff0c;往往会伴随着大量的 malloc 和 free 操作&#xff0c;常常会有很多问题困扰开发者&#xff0c;这个代码会不会发生内存泄漏&#xff1f;会不会重复释放内存&#xff1f;但是在Java开发中我…

python作业二

# 二进制转化为十进制 num input("num:")def binaryToDecimal(binaryString):he 0length len(binaryString)for i in range(length):he int(binaryString[i]) * 2 ** (length - i - 1)return heprint(binaryToDecimal(num))代码运行如下&#xff1a; import math…

数据结构4.0——串的定义和基本操作

串的定义(逻辑结构) 串&#xff0c;即字符串(String)是由零个或多个字符组成的有序数列。 一般记为Sa1a2....an(n>0) 其中&#xff0c;S是串名&#xff0c;单引号括起来的字符序列是串的值;ai可以是字母、数字或其他字符&#xff1b;串中字符的个数n称为串的长度。n0时的…