当前位置:   article > 正文

基于STM32的物联网健康监测系统设计(附源码)_基于stm32的个人健康助手的设计

基于stm32的个人健康助手的设计

目录

1、项目简介

2、整体项目架构设计

2、硬件架构设计

(1)硬件型号

(2)传感器驱动程序设计

3、基于MQTT协议的数据传输

4、微信小程序上位机设计

1.主界面

2. 健康监测系统 

3. 环境监测系统         

4. 显示实时动态曲线         


1、项目简介

        该项目是利用STM32开发板进行开发的基于MQTT协议的物联网健康监测系统,并开发了微信小程序作为该项目的软件上位机。该产品可以用来实时监测人体的心率、血氧等生理参数,也可以监测家庭环境中的温湿度、烟雾浓度等环境参数,从而为您营造出一个健康的居住环境。此项目是本人在寒假利用业余时间开发的,从理论知识的学习到做出最终成品耗时将近两个月,可用于嵌入式软件方向的毕业设计。

        该项目源码已全部开源,在本文中仅介绍核心代码原理,源码已发布在我的GitHub主页,如对源码有疑惑,或者对该项目有改进性意见,可以评论或私信本人,欢迎大家一起交流学习!

        我的github主页:https://github.com/SichengLong26
        源代码地址:https://github.com/SichengLong26/health-iot

2、整体项目架构设计

        该项目主要分为硬件架构设计和软件架构设计,硬件架构设计包括电路设计、PCB焊接、驱动程序编写、数据传输(数据上云)的程序编写;软件架构设计则包括前端UI界面的设计和后端的数据处理,此次软件上位机是基于微信小程序来开发的。

2、硬件架构设计

(1)硬件型号

主控芯片:STM32F103RCT6
传感器:MAX30102心率传感器、DHT11温湿度传感器、MQ2烟雾传感器
通信模块:ESP8266 WIFI模块

(2)传感器驱动程序设计

MAX30102心率血氧传感器(通过IIC驱动)
        MAX3010VCC引脚连接STM32F103mini单片机的5伏引脚,GND连接5伏对应的GND,SCL连PC12,SDA连PC11,INT连PA5。MAX30102的其他引脚没有用到。
        本代码能够正常接收MAX30102心率血氧传感器返回的red与ir的数值,能够比较正常计算出心率血氧数值。当心率或血氧值的计算结果有误时对应的变量值为-999。

main.c

  1. #include "delay.h"
  2. #include "sys.h"
  3. #include "usart.h"
  4. #include "myiic.h"
  5. #include "max30102.h"
  6. #include "algorithm.h"
  7. #define MAX_BRIGHTNESS 255
  8. #define START 100
  9. #define DATA_LENGTH 500
  10. uint32_t aun_ir_buffer[DATA_LENGTH]; //IR LED sensor data
  11. int32_t n_ir_buffer_length; //data length
  12. uint32_t aun_red_buffer[DATA_LENGTH]; //Red LED sensor data
  13. int32_t n_sp02; //SPO2 value
  14. int8_t ch_spo2_valid; //indicator to show if the SP02 calculation is valid
  15. int32_t n_heart_rate; //heart rate value
  16. int8_t ch_hr_valid; //indicator to show if the heart rate calculation is valid
  17. uint8_t uch_dummy;
  18. int main(void)
  19. {
  20. uint32_t un_min, un_max, un_prev_data; //variables to calculate the on-board LED brightness that reflects the heartbeats
  21. int i;
  22. int32_t n_brightness;
  23. float f_temp;
  24. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
  25. delay_init(); //延时函数初始化
  26. uart_init(9600); //串口初始化为115200
  27. IIC_Init();
  28. maxim_max30102_reset(); //resets the MAX30102
  29. // initialize serial communication at 115200 bits per second:
  30. //read and clear status register
  31. maxim_max30102_read_reg(0,&uch_dummy);
  32. maxim_max30102_init(); //initializes the MAX30102
  33. n_brightness=0;
  34. un_min=0x3FFFF;
  35. un_max=0;
  36. n_ir_buffer_length=DATA_LENGTH; //buffer length of 100 stores 5 seconds of samples running at 100sps
  37. //read the first 500 samples, and determine the signal range
  38. for(i=0;i<n_ir_buffer_length;i++)
  39. {
  40. while(PAin(5)==1); //wait until the interrupt pin asserts
  41. maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i)); //read from MAX30102 FIFO
  42. if(un_min>aun_red_buffer[i])
  43. un_min=aun_red_buffer[i]; //update signal min
  44. if(un_max<aun_red_buffer[i])
  45. un_max=aun_red_buffer[i]; //update signal max
  46. printf("心率:%i次/min,", aun_red_buffer[i]/1000);
  47. printf("血氧=%i % \r\n", aun_ir_buffer[i]/1000+16);
  48. }
  49. un_prev_data=aun_red_buffer[i];
  50. //calculate heart rate and SpO2 after first 500 samples (first 5 seconds of samples)
  51. maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
  52. while(1)
  53. {
  54. i=0;
  55. un_min=0x3FFFF;
  56. un_max=0;
  57. //dumping the first 100 sets of samples in the memory and shift the last 400 sets of samples to the top
  58. for(i=START;i<DATA_LENGTH;i++)
  59. {
  60. aun_red_buffer[i-START]=aun_red_buffer[i];
  61. aun_ir_buffer[i-START]=aun_ir_buffer[i];
  62. //update the signal min and max
  63. if(un_min>aun_red_buffer[i])
  64. un_min=aun_red_buffer[i];
  65. if(un_max<aun_red_buffer[i])
  66. un_max=aun_red_buffer[i];
  67. }
  68. //take 100 sets of samples before calculating the heart rate.
  69. for(i=400;i<DATA_LENGTH;i++)
  70. {
  71. un_prev_data=aun_red_buffer[i-1];
  72. while(PAin(5)==1);
  73. maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));
  74. if(aun_red_buffer[i]>un_prev_data)//just to determine the brightness of LED according to the deviation of adjacent two AD data
  75. {
  76. f_temp=aun_red_buffer[i]-un_prev_data;
  77. f_temp/=(un_max-un_min);
  78. f_temp*=MAX_BRIGHTNESS;
  79. n_brightness-=(int)f_temp;
  80. if(n_brightness<0)
  81. n_brightness=0;
  82. }
  83. else
  84. {
  85. f_temp=un_prev_data-aun_red_buffer[i];
  86. f_temp/=(un_max-un_min);
  87. f_temp*=MAX_BRIGHTNESS;
  88. n_brightness+=(int)f_temp;
  89. if(n_brightness>MAX_BRIGHTNESS)
  90. n_brightness=MAX_BRIGHTNESS;
  91. }
  92. //re_oxen=(float)aun_red_buffer[i]/(float)aun_ir_buffer[i];
  93. //oxen=45.06*re_oxen*re_oxen+30.354*re_oxen+94.845;
  94. //send samples and calculation result to terminal program through UART
  95. // printf("red=%i,", aun_red_buffer[i]);
  96. // printf(" ir=%i,", aun_ir_buffer[i]);
  97. }
  98. maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
  99. printf(" HR=%i,", n_heart_rate);
  100. printf(" HRvalid=%i,", ch_hr_valid);
  101. printf(" SpO2=%i,", n_sp02);
  102. printf(" SPO2Valid=%i\r\n", ch_spo2_valid);
  103. }
  104. }

max30102.c(驱动程序)

  1. #include "max30102.h"
  2. #include "myiic.h"
  3. #define max30102_WR_address 0xAE
  4. bool maxim_max30102_write_reg(uint8_t uch_addr, uint8_t uch_data)
  5. /**
  6. * \brief Write a value to a MAX30102 register
  7. * \par Details
  8. * This function writes a value to a MAX30102 register
  9. *
  10. * \param[in] uch_addr - register address
  11. * \param[in] uch_data - register data
  12. *
  13. * \retval true on success
  14. */
  15. {
  16. /* 第1步:发起I2C总线启动信号 */
  17. IIC_Start();
  18. /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  19. IIC_Send_Byte(max30102_WR_address | I2C_WR); /* 此处是写指令 */
  20. /* 第3步:发送ACK */
  21. if (IIC_Wait_Ack() != 0)
  22. {
  23. goto cmd_fail; /* EEPROM器件无应答 */
  24. }
  25. /* 第4步:发送字节地址 */
  26. IIC_Send_Byte(uch_addr);
  27. if (IIC_Wait_Ack() != 0)
  28. {
  29. goto cmd_fail; /* EEPROM器件无应答 */
  30. }
  31. /* 第5步:开始写入数据 */
  32. IIC_Send_Byte(uch_data);
  33. /* 第6步:发送ACK */
  34. if (IIC_Wait_Ack() != 0)
  35. {
  36. goto cmd_fail; /* EEPROM器件无应答 */
  37. }
  38. /* 发送I2C总线停止信号 */
  39. IIC_Stop();
  40. return true; /* 执行成功 */
  41. cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  42. /* 发送I2C总线停止信号 */
  43. IIC_Stop();
  44. return false;
  45. }
  46. bool maxim_max30102_read_reg(uint8_t uch_addr, uint8_t *puch_data)
  47. /**
  48. * \brief Read a MAX30102 register
  49. * \par Details
  50. * This function reads a MAX30102 register
  51. *
  52. * \param[in] uch_addr - register address
  53. * \param[out] puch_data - pointer that stores the register data
  54. *
  55. * \retval true on success
  56. */
  57. {
  58. /* 第1步:发起I2C总线启动信号 */
  59. IIC_Start();
  60. /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  61. IIC_Send_Byte(max30102_WR_address | I2C_WR); /* 此处是写指令 */
  62. /* 第3步:发送ACK */
  63. if (IIC_Wait_Ack() != 0)
  64. {
  65. goto cmd_fail; /* EEPROM器件无应答 */
  66. }
  67. /* 第4步:发送字节地址, */
  68. IIC_Send_Byte((uint8_t)uch_addr);
  69. if (IIC_Wait_Ack() != 0)
  70. {
  71. goto cmd_fail; /* EEPROM器件无应答 */
  72. }
  73. /* 第6步:重新启动I2C总线。下面开始读取数据 */
  74. IIC_Start();
  75. /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  76. IIC_Send_Byte(max30102_WR_address | I2C_RD); /* 此处是读指令 */
  77. /* 第8步:发送ACK */
  78. if (IIC_Wait_Ack() != 0)
  79. {
  80. goto cmd_fail; /* EEPROM器件无应答 */
  81. }
  82. /* 第9步:读取数据 */
  83. {
  84. *puch_data = IIC_Read_Byte(); /* 读1个字节 */
  85. IIC_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
  86. }
  87. /* 发送I2C总线停止信号 */
  88. IIC_Stop();
  89. return true; /* 执行成功 返回data值 */
  90. cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  91. /* 发送I2C总线停止信号 */
  92. IIC_Stop();
  93. return false;
  94. }
  95. bool maxim_max30102_init(void)
  96. /**
  97. * \brief Initialize the MAX30102
  98. * \par Details
  99. * This function initializes the MAX30102
  100. *
  101. * \param None
  102. *
  103. * \retval true on success
  104. */
  105. {
  106. GPIO_InitTypeDef GPIO_InitStructure;
  107. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);//使能PORTA,PORTC时钟
  108. GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//关闭jtag,使能SWD,可以用SWD模式调试
  109. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PA5
  110. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //PA5设置成浮空输入
  111. GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA5
  112. if(!maxim_max30102_write_reg(REG_INTR_ENABLE_1, 0xc0)) // INTR setting
  113. return false;
  114. if(!maxim_max30102_write_reg(REG_INTR_ENABLE_2, 0x00))
  115. return false;
  116. if(!maxim_max30102_write_reg(REG_FIFO_WR_PTR, 0x00)) //FIFO_WR_PTR[4:0]
  117. return false;
  118. if(!maxim_max30102_write_reg(REG_OVF_COUNTER, 0x00)) //OVF_COUNTER[4:0]
  119. return false;
  120. if(!maxim_max30102_write_reg(REG_FIFO_RD_PTR, 0x00)) //FIFO_RD_PTR[4:0]
  121. return false;
  122. if(!maxim_max30102_write_reg(REG_FIFO_CONFIG, 0x6f)) //sample avg = 8, fifo rollover=false, fifo almost full = 17
  123. return false;
  124. if(!maxim_max30102_write_reg(REG_MODE_CONFIG, 0x03)) //0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LED
  125. return false;
  126. if(!maxim_max30102_write_reg(REG_SPO2_CONFIG, 0x2F)) // SPO2_ADC range = 4096nA, SPO2 sample rate (400 Hz), LED pulseWidth (411uS)
  127. return false;
  128. if(!maxim_max30102_write_reg(REG_LED1_PA, 0x17)) //Choose value for ~ 4.5mA for LED1
  129. return false;
  130. if(!maxim_max30102_write_reg(REG_LED2_PA, 0x17)) // Choose value for ~ 4.5mA for LED2
  131. return false;
  132. if(!maxim_max30102_write_reg(REG_PILOT_PA, 0x7f)) // Choose value for ~ 25mA for Pilot LED
  133. return false;
  134. return true;
  135. }
  136. bool maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led)
  137. /**
  138. * \brief Read a set of samples from the MAX30102 FIFO register
  139. * \par Details
  140. * This function reads a set of samples from the MAX30102 FIFO register
  141. *
  142. * \param[out] *pun_red_led - pointer that stores the red LED reading data
  143. * \param[out] *pun_ir_led - pointer that stores the IR LED reading data
  144. *
  145. * \retval true on success
  146. */
  147. {
  148. uint32_t un_temp;
  149. uint8_t uch_temp;
  150. *pun_ir_led = 0;
  151. *pun_red_led = 0;
  152. maxim_max30102_read_reg(REG_INTR_STATUS_1, &uch_temp);
  153. maxim_max30102_read_reg(REG_INTR_STATUS_2, &uch_temp);
  154. /* 第1步:发起I2C总线启动信号 */
  155. IIC_Start();
  156. /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  157. IIC_Send_Byte(max30102_WR_address | I2C_WR); /* 此处是写指令 */
  158. /* 第3步:发送ACK */
  159. if (IIC_Wait_Ack() != 0)
  160. {
  161. goto cmd_fail; /* EEPROM器件无应答 */
  162. }
  163. /* 第4步:发送字节地址, */
  164. IIC_Send_Byte((uint8_t)REG_FIFO_DATA);
  165. if (IIC_Wait_Ack() != 0)
  166. {
  167. goto cmd_fail; /* EEPROM器件无应答 */
  168. }
  169. /* 第6步:重新启动I2C总线。下面开始读取数据 */
  170. IIC_Start();
  171. /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  172. IIC_Send_Byte(max30102_WR_address | I2C_RD); /* 此处是读指令 */
  173. /* 第8步:发送ACK */
  174. if (IIC_Wait_Ack() != 0)
  175. {
  176. goto cmd_fail; /* EEPROM器件无应答 */
  177. }
  178. un_temp = IIC_Read_Byte();
  179. IIC_Ack();
  180. un_temp <<= 16;
  181. *pun_red_led += un_temp;
  182. un_temp = IIC_Read_Byte();
  183. IIC_Ack();
  184. un_temp <<= 8;
  185. *pun_red_led += un_temp;
  186. un_temp = IIC_Read_Byte();
  187. IIC_Ack();
  188. *pun_red_led += un_temp;
  189. un_temp = IIC_Read_Byte();
  190. IIC_Ack();
  191. un_temp <<= 16;
  192. *pun_ir_led += un_temp;
  193. un_temp = IIC_Read_Byte();
  194. IIC_Ack();
  195. un_temp <<= 8;
  196. *pun_ir_led += un_temp;
  197. un_temp = IIC_Read_Byte();
  198. IIC_Ack();
  199. *pun_ir_led += un_temp;
  200. *pun_red_led &= 0x03FFFF; //Mask MSB [23:18]
  201. *pun_ir_led &= 0x03FFFF; //Mask MSB [23:18]
  202. /* 发送I2C总线停止信号 */
  203. IIC_Stop();
  204. return true;
  205. cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  206. /* 发送I2C总线停止信号 */
  207. IIC_Stop();
  208. return false;
  209. }
  210. bool maxim_max30102_reset()
  211. /**
  212. * \brief Reset the MAX30102
  213. * \par Details
  214. * This function resets the MAX30102
  215. *
  216. * \param None
  217. *
  218. * \retval true on success
  219. */
  220. {
  221. if(!maxim_max30102_write_reg(REG_MODE_CONFIG, 0x40))
  222. return false;
  223. else
  224. return true;
  225. }

 alogrihm.c心率血氧算法:

  1. /** \file algorithm.cpp ******************************************************
  2. *
  3. * Project: MAXREFDES117#
  4. * Filename: algorithm.cpp
  5. * Description: This module calculates the heart rate/SpO2 level
  6. *
  7. *
  8. * --------------------------------------------------------------------
  9. *
  10. * This code follows the following naming conventions:
  11. *
  12. * char ch_pmod_value
  13. * char (array) s_pmod_s_string[16]
  14. * float f_pmod_value
  15. * int32_t n_pmod_value
  16. * int32_t (array) an_pmod_value[16]
  17. * int16_t w_pmod_value
  18. * int16_t (array) aw_pmod_value[16]
  19. * uint16_t uw_pmod_value
  20. * uint16_t (array) auw_pmod_value[16]
  21. * uint8_t uch_pmod_value
  22. * uint8_t (array) auch_pmod_buffer[16]
  23. * uint32_t un_pmod_value
  24. * int32_t * pn_pmod_value
  25. *
  26. * ------------------------------------------------------------------------- */
  27. /*******************************************************************************
  28. * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
  29. *
  30. * Permission is hereby granted, free of charge, to any person obtaining a
  31. * copy of this software and associated documentation files (the "Software"),
  32. * to deal in the Software without restriction, including without limitation
  33. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  34. * and/or sell copies of the Software, and to permit persons to whom the
  35. * Software is furnished to do so, subject to the following conditions:
  36. *
  37. * The above copyright notice and this permission notice shall be included
  38. * in all copies or substantial portions of the Software.
  39. *
  40. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  41. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  42. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  43. * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
  44. * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  45. * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  46. * OTHER DEALINGS IN THE SOFTWARE.
  47. *
  48. * Except as contained in this notice, the name of Maxim Integrated
  49. * Products, Inc. shall not be used except as stated in the Maxim Integrated
  50. * Products, Inc. Branding Policy.
  51. *
  52. * The mere transfer of this software does not imply any licenses
  53. * of trade secrets, proprietary technology, copyrights, patents,
  54. * trademarks, maskwork rights, or any other form of intellectual
  55. * property whatsoever. Maxim Integrated Products, Inc. retains all
  56. * ownership rights.
  57. *******************************************************************************
  58. */
  59. #include "algorithm.h"
  60. //uch_spo2_table is approximated as -45.060*ratioAverage* ratioAverage + 30.354 *ratioAverage + 94.845 ;
  61. const uint8_t uch_spo2_table[184] = { 95, 95, 95, 96, 96, 96, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 99, 99, 99, 99,
  62. 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
  63. 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 97, 97,
  64. 97, 97, 96, 96, 96, 96, 95, 95, 95, 94, 94, 94, 93, 93, 93, 92, 92, 92, 91, 91,
  65. 90, 90, 89, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 82, 82, 81, 81,
  66. 80, 80, 79, 78, 78, 77, 76, 76, 75, 74, 74, 73, 72, 72, 71, 70, 69, 69, 68, 67,
  67. 66, 66, 65, 64, 63, 62, 62, 61, 60, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50,
  68. 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 31, 30, 29,
  69. 28, 27, 26, 25, 23, 22, 21, 20, 19, 17, 16, 15, 14, 12, 11, 10, 9, 7, 6, 5,
  70. 3, 2, 1
  71. } ;
  72. void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer, int32_t n_ir_buffer_length, uint32_t *pun_red_buffer, int32_t *pn_spo2, int8_t *pch_spo2_valid,
  73. int32_t *pn_heart_rate, int8_t *pch_hr_valid)
  74. /**
  75. * \brief Calculate the heart rate and SpO2 level
  76. * \par Details
  77. * By detecting peaks of PPG cycle and corresponding AC/DC of red/infra-red signal, the an_ratio for the SPO2 is computed.
  78. * Since this algorithm is aiming for Arm M0/M3. formaula for SPO2 did not achieve the accuracy due to register overflow.
  79. * Thus, accurate SPO2 is precalculated and save longo uch_spo2_table[] per each an_ratio.
  80. *
  81. * \param[in] *pun_ir_buffer - IR sensor data buffer
  82. * \param[in] n_ir_buffer_length - IR sensor data buffer length
  83. * \param[in] *pun_red_buffer - Red sensor data buffer
  84. * \param[out] *pn_spo2 - Calculated SpO2 value
  85. * \param[out] *pch_spo2_valid - 1 if the calculated SpO2 value is valid
  86. * \param[out] *pn_heart_rate - Calculated heart rate value
  87. * \param[out] *pch_hr_valid - 1 if the calculated heart rate value is valid
  88. *
  89. * \retval None
  90. */
  91. {
  92. uint32_t un_ir_mean;
  93. int32_t k, n_i_ratio_count;
  94. int32_t i, n_exact_ir_valley_locs_count, n_middle_idx;
  95. int32_t n_th1, n_npks;
  96. int32_t an_ir_valley_locs[15] ;
  97. int32_t n_peak_interval_sum;
  98. int32_t n_y_ac, n_x_ac;
  99. int32_t n_spo2_calc;
  100. int32_t n_y_dc_max, n_x_dc_max;
  101. int32_t n_y_dc_max_idx, n_x_dc_max_idx;
  102. int32_t an_ratio[5], n_ratio_average;
  103. int32_t n_nume, n_denom ;
  104. // calculates DC mean and subtract DC from ir
  105. un_ir_mean = 0;
  106. for (k = 0 ; k < n_ir_buffer_length ; k++ ) un_ir_mean += pun_ir_buffer[k] ;
  107. un_ir_mean = un_ir_mean / n_ir_buffer_length ;
  108. // remove DC and invert signal so that we can use peak detector as valley detector
  109. for (k = 0 ; k < n_ir_buffer_length ; k++ )
  110. an_x[k] = -1 * (pun_ir_buffer[k] - un_ir_mean) ;
  111. // 4 pt Moving Average
  112. for(k = 0; k < BUFFER_SIZE - MA4_SIZE; k++)
  113. {
  114. an_x[k] = ( an_x[k] + an_x[k + 1] + an_x[k + 2] + an_x[k + 3]) / (int)4;
  115. }
  116. // calculate threshold
  117. n_th1 = 0;
  118. for ( k = 0 ; k < BUFFER_SIZE ; k++)
  119. {
  120. n_th1 += an_x[k];
  121. }
  122. n_th1 = n_th1 / ( BUFFER_SIZE);
  123. if( n_th1 < 30) n_th1 = 30; // min allowed
  124. if( n_th1 > 60) n_th1 = 60; // max allowed
  125. for ( k = 0 ; k < 15; k++) an_ir_valley_locs[k] = 0;
  126. // since we flipped signal, we use peak detector as vSalley detector
  127. maxim_find_peaks( an_ir_valley_locs, &n_npks, an_x, BUFFER_SIZE, n_th1, 4, 15 );//peak_height, peak_distance, max_num_peaks
  128. n_peak_interval_sum = 0;
  129. if (n_npks >= 2)
  130. {
  131. for (k = 1; k < n_npks; k++) n_peak_interval_sum += (an_ir_valley_locs[k] - an_ir_valley_locs[k - 1] ) ;
  132. n_peak_interval_sum = n_peak_interval_sum / (n_npks - 1);
  133. *pn_heart_rate = (int32_t)( (FS * 60) / n_peak_interval_sum );
  134. *pch_hr_valid = 1;
  135. }
  136. else
  137. {
  138. *pn_heart_rate = -999; // unable to calculate because # of peaks are too small
  139. *pch_hr_valid = 0;
  140. }
  141. // load raw value again for SPO2 calculation : RED(=y) and IR(=X)
  142. for (k = 0 ; k < n_ir_buffer_length ; k++ )
  143. {
  144. an_x[k] = pun_ir_buffer[k] ;
  145. an_y[k] = pun_red_buffer[k] ;
  146. }
  147. // find precise min near an_ir_valley_locs
  148. n_exact_ir_valley_locs_count = n_npks;
  149. //using exact_ir_valley_locs , find ir-red DC andir-red AC for SPO2 calibration an_ratio
  150. //finding AC/DC maximum of raw
  151. n_ratio_average = 0;
  152. n_i_ratio_count = 0;
  153. for(k = 0; k < 5; k++) an_ratio[k] = 0;
  154. for (k = 0; k < n_exact_ir_valley_locs_count; k++)
  155. {
  156. if (an_ir_valley_locs[k] > BUFFER_SIZE )
  157. {
  158. *pn_spo2 = -999 ; // do not use SPO2 since valley loc is out of range
  159. *pch_spo2_valid = 0;
  160. return;
  161. }
  162. }
  163. // find max between two valley locations
  164. // and use an_ratio betwen AC compoent of Ir & Red and DC compoent of Ir & Red for SPO2
  165. for (k = 0; k < n_exact_ir_valley_locs_count - 1; k++)
  166. {
  167. n_y_dc_max = -16777216 ;
  168. n_x_dc_max = -16777216;
  169. if (an_ir_valley_locs[k + 1] - an_ir_valley_locs[k] > 3)
  170. {
  171. for (i = an_ir_valley_locs[k]; i < an_ir_valley_locs[k + 1]; i++)
  172. {
  173. if (an_x[i] > n_x_dc_max)
  174. {
  175. n_x_dc_max = an_x[i];
  176. n_x_dc_max_idx = i;
  177. }
  178. if (an_y[i] > n_y_dc_max)
  179. {
  180. n_y_dc_max = an_y[i];
  181. n_y_dc_max_idx = i;
  182. }
  183. }
  184. n_y_ac = (an_y[an_ir_valley_locs[k + 1]] - an_y[an_ir_valley_locs[k] ] ) * (n_y_dc_max_idx - an_ir_valley_locs[k]); //red
  185. n_y_ac = an_y[an_ir_valley_locs[k]] + n_y_ac / (an_ir_valley_locs[k + 1] - an_ir_valley_locs[k]) ;
  186. n_y_ac = an_y[n_y_dc_max_idx] - n_y_ac; // subracting linear DC compoenents from raw
  187. n_x_ac = (an_x[an_ir_valley_locs[k + 1]] - an_x[an_ir_valley_locs[k] ] ) * (n_x_dc_max_idx - an_ir_valley_locs[k]); // ir
  188. n_x_ac = an_x[an_ir_valley_locs[k]] + n_x_ac / (an_ir_valley_locs[k + 1] - an_ir_valley_locs[k]);
  189. n_x_ac = an_x[n_y_dc_max_idx] - n_x_ac; // subracting linear DC compoenents from raw
  190. n_nume = ( n_y_ac * n_x_dc_max) >> 7 ; //prepare X100 to preserve floating value
  191. n_denom = ( n_x_ac * n_y_dc_max) >> 7;
  192. if (n_denom > 0 && n_i_ratio_count < 5 && n_nume != 0)
  193. {
  194. an_ratio[n_i_ratio_count] = (n_nume * 100) / n_denom ; //formular is ( n_y_ac *n_x_dc_max) / ( n_x_ac *n_y_dc_max) ;
  195. n_i_ratio_count++;
  196. }
  197. }
  198. }
  199. // choose median value since PPG signal may varies from beat to beat
  200. maxim_sort_ascend(an_ratio, n_i_ratio_count);
  201. n_middle_idx = n_i_ratio_count / 2;
  202. if (n_middle_idx > 1)
  203. n_ratio_average = ( an_ratio[n_middle_idx - 1] + an_ratio[n_middle_idx]) / 2; // use median
  204. else
  205. n_ratio_average = an_ratio[n_middle_idx ];
  206. if( n_ratio_average > 2 && n_ratio_average < 184)
  207. {
  208. n_spo2_calc = uch_spo2_table[n_ratio_average] ;
  209. *pn_spo2 = n_spo2_calc ;
  210. *pch_spo2_valid = 1;// float_SPO2 = -45.060*n_ratio_average* n_ratio_average/10000 + 30.354 *n_ratio_average/100 + 94.845 ; // for comparison with table
  211. }
  212. else
  213. {
  214. *pn_spo2 = -999 ; // do not use SPO2 since signal an_ratio is out of range
  215. *pch_spo2_valid = 0;
  216. }
  217. }
  218. void maxim_find_peaks( int32_t *pn_locs, int32_t *n_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height, int32_t n_min_distance, int32_t n_max_num )
  219. /**
  220. * \brief Find peaks
  221. * \par Details
  222. * Find at most MAX_NUM peaks above MIN_HEIGHT separated by at least MIN_DISTANCE
  223. *
  224. * \retval None
  225. */
  226. {
  227. maxim_peaks_above_min_height( pn_locs, n_npks, pn_x, n_size, n_min_height );
  228. maxim_remove_close_peaks( pn_locs, n_npks, pn_x, n_min_distance );
  229. *n_npks = min( *n_npks, n_max_num );
  230. }
  231. void maxim_peaks_above_min_height( int32_t *pn_locs, int32_t *n_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height )
  232. /**
  233. * \brief Find peaks above n_min_height
  234. * \par Details
  235. * Find all peaks above MIN_HEIGHT
  236. *
  237. * \retval None
  238. */
  239. {
  240. int32_t i = 1, riseFound = 0, holdOff1 = 0, holdOff2 = 0, holdOffThresh = 4;
  241. *n_npks = 0;
  242. while (i < n_size - 1)
  243. {
  244. if (holdOff2 == 0)
  245. {
  246. if (pn_x[i] > n_min_height && pn_x[i] > pn_x[i - 1]) // find left edge of potential peaks
  247. {
  248. riseFound = 1;
  249. }
  250. if (riseFound == 1)
  251. {
  252. if ((pn_x[i] < n_min_height) && (holdOff1 < holdOffThresh)) // if false edge
  253. {
  254. riseFound = 0;
  255. holdOff1 = 0;
  256. }
  257. else
  258. {
  259. if (holdOff1 == holdOffThresh)
  260. {
  261. if ((pn_x[i] < n_min_height) && (pn_x[i - 1] >= n_min_height))
  262. {
  263. if ((*n_npks) < 15 )
  264. {
  265. pn_locs[(*n_npks)++] = i; // peak is right edge
  266. }
  267. holdOff1 = 0;
  268. riseFound = 0;
  269. holdOff2 = 8;
  270. }
  271. }
  272. else
  273. {
  274. holdOff1 = holdOff1 + 1;
  275. }
  276. }
  277. }
  278. }
  279. else
  280. {
  281. holdOff2 = holdOff2 - 1;
  282. }
  283. i++;
  284. }
  285. }
  286. void maxim_remove_close_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_min_distance)
  287. /**
  288. * \brief Remove peaks
  289. * \par Details
  290. * Remove peaks separated by less than MIN_DISTANCE
  291. *
  292. * \retval None
  293. */
  294. {
  295. int32_t i, j, n_old_npks, n_dist;
  296. /* Order peaks from large to small */
  297. maxim_sort_indices_descend( pn_x, pn_locs, *pn_npks );
  298. for ( i = -1; i < *pn_npks; i++ )
  299. {
  300. n_old_npks = *pn_npks;
  301. *pn_npks = i + 1;
  302. for ( j = i + 1; j < n_old_npks; j++ )
  303. {
  304. n_dist = pn_locs[j] - ( i == -1 ? -1 : pn_locs[i] ); // lag-zero peak of autocorr is at index -1
  305. if ( n_dist > n_min_distance || n_dist < -n_min_distance )
  306. pn_locs[(*pn_npks)++] = pn_locs[j];
  307. }
  308. }
  309. // Resort indices int32_to ascending order
  310. maxim_sort_ascend( pn_locs, *pn_npks );
  311. }
  312. void maxim_sort_ascend(int32_t *pn_x, int32_t n_size)
  313. /**
  314. * \brief Sort array
  315. * \par Details
  316. * Sort array in ascending order (insertion sort algorithm)
  317. *
  318. * \retval None
  319. */
  320. {
  321. int32_t i, j, n_temp;
  322. for (i = 1; i < n_size; i++)
  323. {
  324. n_temp = pn_x[i];
  325. for (j = i; j > 0 && n_temp < pn_x[j - 1]; j--)
  326. pn_x[j] = pn_x[j - 1];
  327. pn_x[j] = n_temp;
  328. }
  329. }
  330. void maxim_sort_indices_descend( int32_t *pn_x, int32_t *pn_indx, int32_t n_size)
  331. /**
  332. * \brief Sort indices
  333. * \par Details
  334. * Sort indices according to descending order (insertion sort algorithm)
  335. *
  336. * \retval None
  337. */
  338. {
  339. int32_t i, j, n_temp;
  340. for (i = 1; i < n_size; i++)
  341. {
  342. n_temp = pn_indx[i];
  343. for (j = i; j > 0 && pn_x[n_temp] > pn_x[pn_indx[j - 1]]; j--)
  344. pn_indx[j] = pn_indx[j - 1];
  345. pn_indx[j] = n_temp;
  346. }
  347. }

DHT11温湿度传感器驱动程序: 

dht11.c

  1. #include "dht11.h"
  2. #include "delay.h"
  3. //由于DHT11为单总线通信,即发送、接收都为同一根数据线,STM32的GPIO无法像51的IO同时配置为输入输出模式,
  4. //因此需要将与DHT11数据线相连的GPIO写两套初始化函数,向DHT11发送数据时先调用DHT11_IO_OUT()函数,再
  5. //发送数据,接收DHT11的数据时先调用DHT11_IO_IN()函数,再接收数据
  6. void DHT11_IO_OUT(void)
  7. {
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. // RCC_APB2PeriphClockCmd(DHT11_GPIO_CLK|RCC_APB2Periph_AFIO, ENABLE); //使能PG端口时钟
  10. //GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //由于PA15为JTAG调试接口,需要先禁用JTAG功能才能作为普通的GPIO口
  11. //使用,若使用的是普通的GPIO,可将 RCC_APB2Periph_AFIO 与 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE) 去掉 //禁用JTAG
  12. GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN; //PG11端口配置
  13. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
  14. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  15. GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStructure); //初始化IO口
  16. }
  17. void DHT11_IO_IN(void)
  18. {
  19. GPIO_InitTypeDef GPIO_InitStructure;
  20. RCC_APB2PeriphClockCmd(DHT11_GPIO_CLK|RCC_APB2Periph_AFIO, ENABLE); //使能PG端口时钟
  21. GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //禁用JTAG
  22. GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN; //PG11端口配置
  23. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //推挽输出
  24. GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStructure); //初始化IO口
  25. }
  26. //复位DHT11
  27. void DHT11_Rst(void)
  28. {
  29. DHT11_IO_OUT(); //SET OUTPUT
  30. DHT11_DQ_OUT=0; //拉低DQ
  31. delay_ms(20); //拉低至少18ms
  32. DHT11_DQ_OUT=1; //DQ=1
  33. delay_us(30); //主机拉高20~40us
  34. }
  35. //等待DHT11的回应
  36. //返回1:未检测到DHT11的存在
  37. //返回0:存在
  38. u8 DHT11_Check(void)
  39. {
  40. u8 retry=0;
  41. DHT11_IO_IN();//SET INPUT
  42. while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
  43. {
  44. retry++;
  45. delay_us(1);
  46. };
  47. if(retry>=100)return 1;
  48. else retry=0;
  49. while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
  50. {
  51. retry++;
  52. delay_us(1);
  53. };
  54. if(retry>=100)return 1;
  55. return 0;
  56. }
  57. //从DHT11读取一个位
  58. //返回值:1/0
  59. u8 DHT11_Read_Bit(void)
  60. {
  61. u8 retry=0;
  62. while(DHT11_DQ_IN&&retry<100)//等待变为低电平
  63. {
  64. retry++;
  65. delay_us(1);
  66. }
  67. retry=0;
  68. while(!DHT11_DQ_IN&&retry<100)//等待变高电平
  69. {
  70. retry++;
  71. delay_us(1);
  72. }
  73. delay_us(40);//等待40us
  74. if(DHT11_DQ_IN)return 1;
  75. else return 0;
  76. }
  77. //从DHT11读取一个字节
  78. //返回值:读到的数据
  79. u8 DHT11_Read_Byte(void)
  80. {
  81. u8 i,dat;
  82. dat=0;
  83. for (i=0;i<8;i++)
  84. {
  85. dat<<=1;
  86. dat|=DHT11_Read_Bit();
  87. }
  88. return dat;
  89. }
  90. //从DHT11读取一次数据
  91. //temp:温度值(范围:0~50°)
  92. //humi:湿度值(范围:20%~90%)
  93. //返回值:0,正常;1,读取失败
  94. u8 DHT11_Read_Data(u8 *temp,u8 *humi)
  95. {
  96. u8 buf[5];
  97. u8 i;
  98. DHT11_Rst();
  99. if(DHT11_Check()==0)
  100. {
  101. for(i=0;i<5;i++)//读取40位数据
  102. {
  103. buf[i]=DHT11_Read_Byte();
  104. }
  105. if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
  106. {
  107. *humi=buf[0];
  108. *temp=buf[2];
  109. }
  110. }else return 1;
  111. return 0;
  112. }
  113. //初始化DHT11的IO口 DQ 同时检测DHT11的存在
  114. //返回1:不存在
  115. //返回0:存在
  116. u8 DHT11_Init(void)
  117. {
  118. DHT11_Rst(); //复位DHT11
  119. return DHT11_Check();//等待DHT11的回应
  120. }

MQ2烟雾传感器驱动程序: 

bsp_adc.c

  1. #include "bsp_adc.h"
  2. #define ADC1_DR_Address ((u32)0x40012400+0x4c) //定义ADC的内存地址
  3. #include <math.h>
  4. static int floag1=0;
  5. #define CAL_PPM 20 // 校准环境中PPM值
  6. #define RL 5 // RL阻值
  7. static float R0=1; // 元件在洁净空气中的阻值
  8. float ppm;
  9. __IO uint16_t ADC_ConvertedValue;
  10. static void ADC1_GPIO_Config(void) //ADC端口配置
  11. {
  12. GPIO_InitTypeDef GPIO_InitStructure;//GPIO初始化结构体;
  13. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA的时钟;
  14. RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC, ENABLE);//打开ADC1和GPIOC的时钟;
  15. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//配置PC0引脚;
  16. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//设置工作模式为模拟输入;
  17. GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOC;
  18. }
  19. static void ADC1_Mode_Config(void) //配置ADC1的模式
  20. {
  21. /********以下是有关DMA的相关配置*************/
  22. DMA_InitTypeDef DMA_InitStructure;//DMA初始化结构体定义DMA初始化变量
  23. ADC_InitTypeDef ADC_InitStructure;//ADC初始化结构体定义ADC初始化变量
  24. DMA_DeInit(DMA1_Channel1);//设置DMA1通道1
  25. DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;//设定ADC的地址;
  26. DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_ConvertedValue;//内存地址,采集的数据存在这里;
  27. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//设为源,表示数据是从这里出发的;
  28. DMA_InitStructure.DMA_BufferSize = 1;//因为一次只发送一个数据所以设为1;
  29. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;//因为只涉及一路数据的采集发送因此内存地址不变
  30. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//因为只涉及一路数据的采集发送因此外设地址不变
  31. DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设至少要半字即16位才可以满足要求
  32. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//内存至少要半字即16位才可以满足要求
  33. DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA模式为循环传输,因为要采集多次;
  34. DMA_InitStructure.DMA_Priority = DMA_Priority_High;//设置为高、中、低优先级都可以因为只有一路在采集
  35. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//关闭内存到内存的传输,因为我们需要的是外设传到内存的传输
  36. DMA_Init(DMA1_Channel1,&DMA_InitStructure);//DMA1通道1最后初始化
  37. DMA_Cmd(DMA1_Channel1, ENABLE);//使能DMA1的通道1;
  38. /********以下是有关ADC的相关配置*************/
  39. ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//设置为独立ADC模式,因为其采集只有一个通道;
  40. ADC_InitStructure.ADC_ScanConvMode = DISABLE;//禁止扫描模式,扫描模式适用于多通道采集
  41. ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//开启连续转换,以不停地进行ADC转换
  42. ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部触发转换,而使用内部软件触发
  43. ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//采集数据右对齐
  44. ADC_InitStructure.ADC_NbrOfChannel = 1;//ADC number of channel,即要转换的通道数目;
  45. ADC_Init(ADC1, &ADC_InitStructure);//调用ADC初始化库函数
  46. RCC_ADCCLKConfig(RCC_PCLK2_Div8);//配置ADC时钟为8分频,9MHZ
  47. ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_55Cycles5);//配置ADC1的通道为55.5个采样周期,
  48. ADC_DMACmd(ADC1, ENABLE);//使能ADC1的DMA传输
  49. ADC_Cmd(ADC1, ENABLE);//使能ADC1;
  50. while(ADC_GetResetCalibrationStatus(ADC1));//等待校准寄存器复位完成;
  51. ADC_StartCalibration(ADC1);//调用校准函数开始ADC校准;
  52. while(ADC_GetResetCalibrationStatus(ADC1));//等待校准寄存器复位完成;
  53. ADC_SoftwareStartConvCmd(ADC1, ENABLE);//前面不采用外部触发,而是采用内部软件触发,此处使能软件触发
  54. }
  55. void ADC1_Init(void)
  56. {
  57. ADC1_GPIO_Config();
  58. ADC1_Mode_Config();
  59. }
  60. // 传感器校准函数
  61. void MQ2_PPM_Calibration(float RS)
  62. {
  63. R0 = RS / pow(CAL_PPM / 613.9f, 1 / -2.074f);
  64. }
  65. // MQ2传感器数据处理
  66. float MQ2_GetPPM(void)
  67. {
  68. float Vrl = (float) ADC_ConvertedValue/4096*3.3;
  69. float RS = (3.3f - Vrl) / Vrl * RL;
  70. if(Vrl>1&&floag1==0) // 获取系R0
  71. {
  72. MQ2_PPM_Calibration(RS);
  73. floag1=1;
  74. }
  75. ppm = 613.9f * pow(RS/R0, -2.074f);
  76. return ppm;
  77. }

3、基于MQTT协议的数据传输

        STM32开发板通过ESP8266模块连接上局域网(也就是我们家庭中的路由器、WIFI),同时ESP8266通过互联网连接到远程MQTT服务器(MQTT Server),这样便达到了将传感器所采集到的数据储存在云端,可供多个客户端进行访问。
         在数据传输中,用到了一种很核心的物联网数据传输协议:MQTT协议。MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议。它的设计思想是轻巧、开放、简单、规范,易于实现。这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。它的基本原理是:一个客户端(ESP8266)向服务器发布(Publish)一个带有传感采集到的数据的主题,则另外一个客户端(软件上位机)则通过向服务器订阅(Subscribe)服务器上ESP8266客户端发布的主题,即可以接收到各传感器采集到的数据,从而达到实时显示、实时检测的目的。
        同时数据在传输的过程中都是以JSON格式进行传输的。

 核心代码介绍:

        由于该部分核心代码过多,就不在本文展示,请到我的github源码仓库进行访问,在这里简要对一些程序文件进行说明:

esp8266.c : ESP8266模块的驱动程序
onenet.c : 数据上传云域网程序
MqttKit.c : 客户端对服务器进行一系列数据传输程序(例如主题的发布、订阅)

4、微信小程序上位机设计

        小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验。同时因为它不像安卓APP一样需要下载,可以免安装使用,有一定的便利性,所以本项目采用微信小程序来开发上位机。下面该软件上位机的各个UI界面及其所对应的功能

1.主界面

        这是该软件的首页,通过和风天气平台的支持,可以获取到当前你所在地的天气状况,同时正中间的三个控件可以进入三个不同的系统。

代码:

index.wxml:

  1. <!--pages/home/home.wxml-->
  2. <view class="wrapper">
  3. <view class="header-wrapper" bindtap="toDetail">
  4. <view class="header-title">
  5. <text>空气质量-{{weather_quality}}</text>
  6. <text>{{city}}-{{area}}</text>
  7. </view>
  8. <view class="header-text">
  9. <text>{{weather_temp}}℃</text>
  10. <text>{{weather_text}}</text>
  11. </view>
  12. <view class="weather-advice">
  13. <text>{{advice}}</text>
  14. </view>
  15. </view>
  16. <view class="botton-wrapper">
  17. <button id="btn1" bindtap="toEnv">环境监测系统</button>
  18. <button id="btn2" bindtap="toHealth">健康监测系统</button>
  19. <button id="btn2" bindtap="toCanvas">查看实时动态曲线</button>
  20. </view>
  21. </view>

 index.wxss

  1. /* pages/home/home.wxss */
  2. .wrapper{
  3. padding: 30rpx 20rpx;
  4. /* background-color: beige; */
  5. }
  6. .header-wrapper{
  7. background-color: #3d7ef6;
  8. border-radius: 40rpx;
  9. padding: 30rpx 50rpx;
  10. box-shadow: #d6d6d6 1px 1px 1px;
  11. color: floralwhite;
  12. }
  13. .header-title{
  14. display: flex;
  15. font-size: 30rpx;
  16. justify-content: space-between;
  17. }
  18. .header-text{
  19. display: flex;
  20. font-size: 48rpx;
  21. font-weight: 400;
  22. padding: 7rpx 0rpx;
  23. justify-content: space-between;
  24. }
  25. .weather-advice{
  26. font-size: 26rpx;
  27. margin-top: 50rpx;
  28. }
  29. .botton-wrapper{
  30. padding: 240rpx 30rpx;
  31. }
  32. #btn1{
  33. height: 80rpx;
  34. width: 60% ;
  35. color: white;
  36. font-size: 30rpx ;
  37. background-color: #3d7ef6 ;
  38. margin-top: 5rpx;
  39. justify-content: center;
  40. box-shadow: #d6d6d6 2px 2px 2px;
  41. border-radius: 40rpx;
  42. }
  43. #btn2{
  44. height: 80rpx;
  45. width: 60% ;
  46. color: white;
  47. font-size: 30rpx ;
  48. background-color: #3d7ef6 ;
  49. margin-top: 50rpx;
  50. justify-content: center;
  51. box-shadow: #d6d6d6 2px 2px 2px;
  52. border-radius: 40rpx;
  53. }

 index.js

  1. Page({
  2. /**
  3. * 页面的初始数据
  4. */
  5. data: {
  6. weather_quality:"请求中",
  7. weather_text:"请求中",
  8. weather_temp:"请求中",
  9. city:"请求中",
  10. area:"请求中",
  11. advice:"请求中"
  12. },
  13. toEnv(){
  14. wx.navigateTo({
  15. url: '../env/env'
  16. })
  17. },
  18. toHealth(){
  19. wx.navigateTo({
  20. url: '../healthy/healthy'
  21. })
  22. },
  23. toCanvas(){
  24. wx.navigateTo({
  25. url: '../curves/curves'
  26. })
  27. },
  28. toDetail(){
  29. wx.navigateTo({
  30. url: '../weather/weather'
  31. })
  32. },
  33. toBluetooth(){
  34. wx.navigateTo({
  35. url: '../bluetooth/bluetooth'
  36. })
  37. },
  38. /**
  39. * 生命周期函数--监听页面加载
  40. */
  41. onLoad: function (options) {
  42. },
  43. onShow: function () {
  44. var that=this
  45. wx.getLocation({
  46. success(res){
  47. const {latitude} = res
  48. const {longitude} = res
  49. const key = '1c6a3dc86f2544a3b18828ca409858c9'
  50. // 请求温度与天气状况
  51. wx.request({
  52. url: `https://devapi.qweather.com/v7/weather/now?location=${longitude},${latitude}&key=${key}`,
  53. success(res){
  54. // console.log(res)
  55. that.setData({
  56. weather_temp:res.data.now.temp,
  57. weather_text:res.data.now.text
  58. })
  59. }
  60. })
  61. // 地理位置
  62. wx.request({
  63. url: `https://geoapi.qweather.com/v2/city/lookup?location=${longitude},${latitude}&key=${key}`,
  64. success(res){
  65. // console.log(res)
  66. that.setData({
  67. city:res.data.location[0].adm2,
  68. area:res.data.location[0].name
  69. })
  70. }
  71. })
  72. //指数
  73. wx.request({
  74. url: `https://devapi.qweather.com/v7/indices/1d?location=${longitude},${latitude}&key=${key}&type=${0}`,
  75. success(res){
  76. // console.log(res)
  77. that.setData({
  78. advice:res.data.daily[2].text
  79. })
  80. }
  81. })
  82. //空气质量
  83. wx.request({
  84. url: `https://devapi.qweather.com/v7/air/now?location=${longitude},${latitude}&key=${key}`,
  85. success(res){
  86. console.log(res)
  87. that.setData({
  88. weather_quality:res.data.now.category
  89. })
  90. }
  91. })
  92. }
  93. })
  94. },
  95. /**
  96. * 生命周期函数--监听页面隐藏
  97. */
  98. onHide: function () {
  99. },
  100. /**
  101. * 生命周期函数--监听页面卸载
  102. */
  103. onUnload: function () {
  104. },
  105. /**
  106. * 页面相关事件处理函数--监听用户下拉动作
  107. */
  108. onPullDownRefresh: function () {
  109. },
  110. /**
  111. * 页面上拉触底事件的处理函数
  112. */
  113. onReachBottom: function () {
  114. },
  115. /**
  116. * 用户点击右上角分享
  117. */
  118. onShareAppMessage: function () {
  119. }
  120. })

2. 健康监测系统 

用于显示MAX30102心率血氧传感器所采集到的人体的心率、血氧值,可以对其进行实时监测并显示

healthy.wxml :

  1. <view class="body-wrapper">
  2. <view class="sensor-others">
  3. <image class="sensor-logo-water" src="../../icon/heart.png"/>
  4. <view class="sensor-text">
  5. <view class="sensor-title">实时心率</view>
  6. <view class="sensor-value">{{Heart}}</view>
  7. </view>
  8. </view>
  9. </view>
  10. <view class="body-wrapper">
  11. <view class="sensor-others">
  12. <image class="sensor-logo-oxygen" src="../../icon/oxygen.png"/>
  13. <view class="sensor-text">
  14. <view class="sensor-title">血氧浓度</view>
  15. <view class="sensor-value">{{Spo2}}%</view>
  16. </view>
  17. </view>
  18. </view>

healty.wxss: 

  1. /* pages/healthy/healthy.wxss *//* pages/env/env.wxss */
  2. .body-wrapper{
  3. padding: 30rpx 20rpx;
  4. }
  5. .sensor{
  6. width: 100%;
  7. height: 190rpx;
  8. border-radius: 40rpx;
  9. box-shadow: #d6d6d6 1px 1px 5px;
  10. margin-top: 50rpx;
  11. display: flex;
  12. justify-content: space-between;
  13. }
  14. .sensor-logo{
  15. padding: 20rpx 30rpx;
  16. height: 130rpx;
  17. width: 190rpx;
  18. margin-top: 10rpx;
  19. }
  20. .sensor-logo-water{
  21. padding: 20rpx 50rpx;
  22. height: 130rpx;
  23. width: 180rpx;
  24. margin-top: 10rpx;
  25. }
  26. .sensor-logo-oxygen{
  27. padding: 20rpx 90rpx;
  28. height: 118rpx;
  29. width: 120rpx;
  30. margin-top: 10rpx;
  31. }
  32. .sensor-text{
  33. padding: 0rpx 120rpx;
  34. margin-top: 26rpx;
  35. color: #2e2e2e;
  36. }
  37. .sensor-title{
  38. font-size: 35rpx;
  39. }
  40. .sensor-value{
  41. font-size: 66rpx;
  42. }
  43. .sensor-others{
  44. width: 100%;
  45. height: 190rpx;
  46. border-radius: 40rpx;
  47. box-shadow: #d6d6d6 1px 1px 5px;
  48. margin-top: 0rpx;
  49. display: flex;
  50. justify-content: space-between;
  51. }

healty.js 

  1. var mqtt=require('../../utils/mqtt.min.js')
  2. let client=null
  3. Page({
  4. data: {
  5. Heart:0,
  6. Spo2:0
  7. },
  8. /**
  9. * 生命周期函数--监听页面加载
  10. */
  11. onLoad: function (options) {
  12. this.connectmqtt()
  13. },
  14. connectmqtt:function(){
  15. var that=this
  16. const options={
  17. connectTimeout:4000,
  18. clientId:"d1f22er224",
  19. port:8084,
  20. username:'f585c3d5f499ffea9b710f13709a855d',
  21. password:'123456'
  22. }
  23. client=mqtt.connect('wxs://t.yoyolife.fun/mqtt',options)
  24. client.on('connect',(e)=>{
  25. console.log("mqtt服务器连接成功")
  26. client.subscribe('/iot/943/pub',
  27. {qos:0},function(err){
  28. if(!err){
  29. console.log("订阅成功!")
  30. }
  31. })
  32. })
  33. client.on('message',function(topic,message){
  34. let dataFrameDev=[]
  35. dataFrameDev=JSON.parse(message)
  36. console.log(dataFrameDev)
  37. })
  38. }
  39. })

3. 环境监测系统         

        用于显示DHT11温湿度传感器、MQ2传感器所采集到的环境参数数据,可以对其进行实时监测并显示。

env.wxml:

  1. <!--pages/env/env.wxml-->
  2. <view class="body-wrapper">
  3. <view class="sensor">
  4. <image class="sensor-logo" src="../../icon/temperature.png"/>
  5. <view class="sensor-text">
  6. <view class="sensor-title">实时温度</view>
  7. <view class="sensor-value">{{Temp}}℃</view>
  8. </view>
  9. </view>
  10. </view>
  11. <view class="body-wrapper">
  12. <view class="sensor-others">
  13. <image class="sensor-logo-water" src="../../icon/water.png"/>
  14. <view class="sensor-text">
  15. <view class="sensor-title">实时湿度</view>
  16. <view class="sensor-value">{{Hum}}%</view>
  17. </view>
  18. </view>
  19. </view>
  20. <view class="body-wrapper">
  21. <view class="sensor-others">
  22. <image class="sensor-logo-smoke" src="../../icon/smoke.png"/>
  23. <view class="sensor-text">
  24. <view class="sensor-title">烟雾浓度</view>
  25. <view class="sensor-value">{{Smoke}}</view>
  26. </view>
  27. </view>
  28. </view>
  29. <view class="body-wrapper">
  30. <view class="sensor-others">
  31. <image class="sensor-logo" src="../../icon/lx.png"/>
  32. <view class="sensor-text">
  33. <view class="sensor-title">光照度</view>
  34. <view class="sensor-value">29lx</view>
  35. </view>
  36. </view>
  37. </view>
  38. <view class="control-wrapper">
  39. <view class="body-wrapper">
  40. <view class="control">
  41. <image class="control-logo" src="../../icon/beep.png"/>
  42. <view class="control-text">
  43. <view class="control-title">报警器</view>
  44. <view class="control-value">
  45. <switch bindchange="handleLED" checked="{{true}}"></switch>
  46. </view>
  47. </view>
  48. </view>
  49. </view>
  50. <view class="body-wrapper">
  51. <view class="control">
  52. <image class="control-logo" src="../../icon/led.png"/>
  53. <view class="control-text">
  54. <view class="control-title">房间灯</view>
  55. <view class="control-value">
  56. <switch bindchange="handleLED" checked="{{true}}"></switch>
  57. </view>
  58. </view>
  59. </view>
  60. <view>{{event}}</view>
  61. </view>
  62. </view>

 env.wxss

  1. /* pages/env/env.wxss */
  2. .body-wrapper{
  3. padding: 30rpx 20rpx;
  4. }
  5. .sensor{
  6. width: 100%;
  7. height: 190rpx;
  8. border-radius: 40rpx;
  9. box-shadow: #d6d6d6 1px 1px 5px;
  10. margin-top: 50rpx;
  11. display: flex;
  12. justify-content: space-between;
  13. }
  14. .sensor-logo{
  15. padding: 20rpx 30rpx;
  16. height: 130rpx;
  17. width: 190rpx;
  18. margin-top: 10rpx;
  19. }
  20. .sensor-logo-water{
  21. padding: 20rpx 50rpx;
  22. height: 130rpx;
  23. width: 180rpx;
  24. margin-top: 10rpx;
  25. }
  26. .sensor-logo-smoke{
  27. padding: 20rpx 50rpx;
  28. height: 130rpx;
  29. width: 140rpx;
  30. margin-top: 10rpx;
  31. }
  32. .sensor-text{
  33. padding: 0rpx 120rpx;
  34. margin-top: 26rpx;
  35. color: #2e2e2e;
  36. }
  37. .sensor-title{
  38. font-size: 35rpx;
  39. }
  40. .sensor-value{
  41. font-size: 66rpx;
  42. }
  43. .sensor-others{
  44. width: 100%;
  45. height: 190rpx;
  46. border-radius: 40rpx;
  47. box-shadow: #d6d6d6 1px 1px 5px;
  48. margin-top: 0rpx;
  49. display: flex;
  50. justify-content: space-between;
  51. }
  52. .control{
  53. width: 100%;
  54. height: 190rpx;
  55. border-radius: 40rpx;
  56. box-shadow: #d6d6d6 1px 1px 5px;
  57. margin-top: 0rpx;
  58. display: flex;
  59. justify-content: space-between;
  60. }
  61. .control-logo{
  62. padding: 40rpx 30rpx;
  63. height: 100rpx;
  64. width: 100rpx;
  65. margin-top: 10rpx;
  66. }
  67. .control-text{
  68. padding: 0rpx 30rpx;
  69. margin-top: 26rpx;
  70. color: #2e2e2e;
  71. }
  72. .control-title{
  73. font-size: 33rpx;
  74. }
  75. .control-value{
  76. font-size: 60rpx;
  77. }
  78. .control-wrapper{
  79. display: flex;
  80. padding: 0rpx 19.5rpx;
  81. }

 env.js

  1. var mqtt=require('../../utils/mqtt.min.js')
  2. var client=null
  3. Page({
  4. /**
  5. * 页面的初始数据
  6. */
  7. data: {
  8. Temp:0,
  9. Hum:0,
  10. Smoke:0,
  11. Led:false,
  12. Beep:false,
  13. event:""
  14. },
  15. /**
  16. * 生命周期函数--监听页面加载
  17. */
  18. onLoad(){
  19. this.connectmqtt()
  20. },
  21. connectmqtt:function(){
  22. var that=this
  23. const options={
  24. connectTimeout:4000,
  25. clientId:"df2er24",
  26. port:8084,
  27. username:'f585c3d5f499ffea9b710f13709a855d',
  28. password:'123456'
  29. }
  30. client=mqtt.connect('wxs://t.yoyolife.fun/mqtt',options)
  31. client.on('connect',(e)=>{
  32. console.log("服务器连接成功")
  33. client.subscribe('/iot/943/pub',{qos:0},function(err){
  34. if(!err){
  35. console.log("订阅成功")
  36. }
  37. })
  38. })
  39. // 信息监听事件
  40. client.on('message', function(topic,message){
  41. // console.log(topic)
  42. let dataFrameDev ={}
  43. dataFrameDev = JSON.parse(message)
  44. console.log(dataFrameDev)
  45. that.setData({
  46. Temp:dataFrameDev.Temp,
  47. Hum:dataFrameDev.Hum,
  48. Smoke:dataFrameDev.Smoke
  49. })
  50. console.log(that.data.Temp)
  51. })
  52. client.on('reconnect', (error)=>{
  53. console.log('正在重新连接'+error)
  54. })
  55. client.on('error', (error)=>{
  56. console.log('连接失败')
  57. })
  58. },
  59. handleLED(e){
  60. var that=this
  61. // console.log(e)
  62. let {value}=e.detail
  63. // console.log(value)
  64. that.setData({
  65. Led:value,
  66. })
  67. if(value===true){
  68. that.setData({
  69. event:"您已开灯!",
  70. })
  71. client.publish('/iot/943/sub','{"target":"LED","value":1}',function(err){
  72. if(!err){
  73. console.log("成功发布开灯命令")
  74. }
  75. })
  76. }else{
  77. that.setData({
  78. event:""
  79. })
  80. client.publish('/iot/943/sub','{"target":"LED","value":0}',function(err){
  81. if(!err){
  82. console.log("成功发布关灯命令")
  83. }
  84. })
  85. }
  86. }
  87. })

4. 显示实时动态曲线         

        为了能显示出一段时间内各个参数的动态变化过程,这里开发了一个实时动态曲线功能,每当接收到服务端传来的消息,则刷新一次曲线从而达到实时更新的目的。

curves.wxml

  1. <Tabs list="{{list}}" binditemChange="handleItemChange">
  2. <block wx:if="{{list[0].isActive}}" class="tabs">
  3. <view class="body">
  4. <view class="body-content">
  5. <view class="body-title">温湿度、烟雾浓度实时曲线图</view>
  6. <view class="meandata-body">
  7. <view class="meandata">
  8. <view>
  9. <view>平均浓度</view>
  10. <view>{{smoke_average}}bpm</view>
  11. </view>
  12. <view>
  13. <view>平均湿度</view>
  14. <view>{{hum_average}}%</view>
  15. </view>
  16. <view>
  17. <view>平均温度</view>
  18. <view>{{temp_average}}℃</view>
  19. </view>
  20. </view>
  21. </view>
  22. <canvas canvas-id="lineCanvas" disable-scroll="true" class="canvas" bindtouchstart="touchHandler"></canvas>
  23. <view class="timestyle">{{time}}</view>
  24. </view>
  25. </view>
  26. </block>
  27. <block wx:elif="{{list[1].isActive}}">
  28. <view class="detail-title">
  29. <view class="title-time">时间</view>
  30. <view class="title-temp">温度</view>
  31. <view class="title-hum">湿度</view>
  32. <view class="title-smoke">烟雾浓度</view>
  33. </view>
  34. <view class="data">
  35. <view class="data-time">
  36. <view wx:for="{{time_array}}" wx:key="*this">{{item}}</view>
  37. </view>
  38. <view class="data-temp">
  39. <view wx:for="{{Temp_array}}" wx:key="*this" class="data-temp">{{item}}℃</view>
  40. </view>
  41. <view class="data-hum">
  42. <view wx:for="{{Hum_array}}" wx:key="*this" class="data-hum">{{item}}%</view>
  43. </view>
  44. <view class="data-smoke">
  45. <view wx:for="{{Smoke_array}}" wx:key="*this" class="data-smoke">{{item}}bpm</view>
  46. </view>
  47. </view>
  48. </block>
  49. </Tabs>

curves.wxss 

  1. page {
  2. background-color: rgba(239, 239, 240);
  3. }
  4. .body{
  5. padding: 0rpx 20rpx;
  6. margin-top: 100rpx;
  7. }
  8. .body-content{
  9. background-color:#ffffff;
  10. border-radius: 40rpx;
  11. }
  12. .body-title{
  13. display: flex;
  14. justify-content: center;
  15. font-size: 30rpx;
  16. padding: 50rpx 0rpx 20rpx;
  17. }
  18. .meandata-body{
  19. padding: 20rpx 39rpx;
  20. }
  21. .meandata{
  22. display: flex;
  23. justify-content: space-around;
  24. border-bottom: 1rpx solid rgba(216, 216, 216, 1);
  25. border-top: 1rpx solid rgba(216, 216, 216, 1);
  26. padding: 24rpx;
  27. }
  28. .canvas {
  29. width: 100%;
  30. height: 550rpx;
  31. }
  32. .timestyle{
  33. padding: 50rpx 165rpx;
  34. }
  35. .detail-title{
  36. display: flex;
  37. }
  38. .data-temp{
  39. padding: 19rpx 30rpx;
  40. }
  41. .data-hum{
  42. padding: 19rpx 30rpx;
  43. }
  44. .data-smoke{
  45. padding: 19rpx 30rpx;
  46. }
  47. .data{
  48. display: flex;
  49. }
  50. .title-time{
  51. padding: 0rpx 0rpx 0rpx 60rpx;
  52. }
  53. .title-temp{
  54. padding: 0rpx 0rpx 0rpx 111rpx;
  55. }
  56. .title-hum{
  57. padding: 0rpx 0rpx 0rpx 117rpx;
  58. }
  59. .title-smoke{
  60. padding: 0rpx 0rpx 0rpx 140rpx;
  61. }

curves.js

  1. // pages/index/lookrecord/lookrecord.js
  2. var wxCharts = require('../../utils/wxcharts.js'); //引入wxChart文件
  3. var mqtt=require('../../utils/mqtt.min.js') // 引入mqtt文件
  4. var util = require("../../utils/util.js");
  5. var client=null
  6. var app = getApp();
  7. var lineChart = null;
  8. Page({
  9. /**
  10. * 页面的初始数据
  11. */
  12. data: {
  13. list:[
  14. {
  15. id:0,
  16. name:"趋势图",
  17. isActive:true
  18. },{
  19. id:1,
  20. name:"数据记录",
  21. isActive:false
  22. },
  23. ],
  24. time:"",
  25. xtime:"",
  26. Temp:0,
  27. temp_average:0,
  28. temp_sum:0,
  29. Hum:0,
  30. hum_average:0,
  31. hum_sum:0,
  32. Smoke:0,
  33. smoke_average:0,
  34. smoke_sum:0,
  35. Temp_array:[],
  36. Hum_array:[],
  37. Smoke_array:[],
  38. time_array:[],
  39. waterwaterdata:[50, 100, 80, 115, 120, 90, 125],
  40. smokesmokedata:[60, 70, 90, 105, 120, 130, 95],
  41. tempdata: [60,90, 60, 110,120,105,70], //数据点
  42. categories: ['2018-6-13', '2018-6-14', '2018-6-15', '2018-6-16', '2018-6-17', '2018-6-18', '2018-6-19'], //模拟的x轴横坐标参数
  43. },
  44. touchHandler: function (e) {
  45. lineChart.showToolTip(e, {
  46. // background: '#7cb5ec',
  47. format: function (item, category) {
  48. return category + ' ' + item.name + ':' + item.data
  49. }
  50. });
  51. },
  52. /**
  53. * 生命周期函数--监听页面加载
  54. */
  55. onLoad(){
  56. this.curve()
  57. this.connectmqtt()
  58. // this.gettime()
  59. },
  60. onShow:function(){
  61. this.notification() // 调用方法
  62. },
  63. connectmqtt:function(){
  64. var that=this
  65. const options={
  66. connectTimeout:4000,
  67. clientId:"df2er24",
  68. port:8084,
  69. username:'f585c3d5f499ffea9b710f13709a855d',
  70. password:'123456'
  71. }
  72. client=mqtt.connect('wxs://t.yoyolife.fun/mqtt',options)
  73. client.on('connect',(e)=>{
  74. console.log("服务器连接成功")
  75. client.subscribe('/iot/943/pub',{qos:0},function(err){
  76. if(!err){
  77. console.log("订阅成功")
  78. }
  79. })
  80. })
  81. // 信息监听事件
  82. client.on('message', function(topic,message){
  83. // console.log(topic)
  84. let dataFrameDev ={}
  85. dataFrameDev = JSON.parse(message)
  86. console.log(dataFrameDev)
  87. that.setData({
  88. Temp:dataFrameDev.Temp,
  89. Hum:dataFrameDev.Hum,
  90. Smoke:dataFrameDev.Smoke,
  91. })
  92. // 设置温度、湿度、烟雾浓度的数组
  93. that.setData({
  94. Temp_array:that.data.Temp_array.concat(that.data.Temp),
  95. Hum_array:that.data.Hum_array.concat(that.data.Hum),
  96. Smoke_array:that.data.Smoke_array.concat(that.data.Smoke)
  97. })
  98. console.log(that.data.Temp)
  99. console.log(that.data.Temp_array)
  100. console.log(that.data.Hum_array)
  101. console.log(that.data.Smoke_array)
  102. // 获取到sensor data 后开始获取本地时间
  103. var xtime=that.data.xtime
  104. that.setData({
  105. xtime:util.formatTime(new Date())
  106. })
  107. console.log(that.data.xtime)
  108. that.setData({
  109. time_array:that.data.time_array.concat(that.data.xtime)
  110. })
  111. // 求烟雾浓度平均值
  112. var smoke_average=that.data.smoke_average
  113. var Smoke_array=that.data.Smoke_array
  114. var smoke_sum=that.data.smoke_sum
  115. that.setData({
  116. smoke_sum:smoke_sum+that.data.Smoke,
  117. smoke_average:parseInt(that.data.smoke_sum/(Smoke_array.length))
  118. })
  119. console.log(that.data.time_array)
  120. console.log("平均浓度"+that.data.smoke_average)
  121. // 求温度平均值
  122. var temp_average=that.datatempe_average
  123. var Temp_array=that.data.Temp_array
  124. var temp_sum=that.data.temp_sum
  125. that.setData({
  126. temp_sum:temp_sum+that.data.Temp,
  127. temp_average:parseInt(that.data.temp_sum/(Temp_array.length))
  128. })
  129. console.log(that.data.time_array)
  130. console.log("平均温度"+that.data.temp_average)
  131. // 求平均湿度
  132. var hum_average=that.data.hum_average
  133. var Hum_array=that.data.Hum_array
  134. var hum_sum=that.data.hum_sum
  135. that.setData({
  136. hum_sum:hum_sum+that.data.Hum,
  137. hum_average:parseInt(that.data.hum_sum/(Hum_array.length))
  138. })
  139. console.log(that.data.time_array)
  140. console.log("平均湿度"+that.data.hum_average)
  141. })
  142. client.on('reconnect', (error)=>{
  143. console.log('正在重新连接'+error)
  144. })
  145. client.on('error', (error)=>{
  146. console.log('连接失败')
  147. })
  148. },
  149. curve (e) {
  150. var that=this
  151. var windowWidth = '', windowHeight=''; //定义宽高
  152. that.data.setInter = setInterval(function(){
  153. var waterwaterdata=that.data.Hum_array
  154. var smokesmokedata=that.data.Smoke_array
  155. var tempdata=that.data.Temp_array
  156. var categories=that.data.time_array
  157. try {
  158. var res = wx.getSystemInfoSync(); //试图获取屏幕宽高数据
  159. windowWidth = res.windowWidth / 750 * 690; //以设计图750为主进行比例算换
  160. windowHeight = res.windowWidth / 750 * 550 //以设计图750为主进行比例算换
  161. } catch (e) {
  162. console.error('getSystemInfoSync failed!'); //如果获取失败
  163. }
  164. lineChart = new wxCharts({ //定义一个wxCharts图表实例
  165. canvasId: 'lineCanvas', //输入wxml中canvas的id
  166. type: 'line', //图标展示的类型有:'line','pie','column','area','ring','radar'
  167. categories: categories,
  168. animation: true, //是否开启动画
  169. series: [{ //具体坐标数据
  170. name: '温度', //名字
  171. data: tempdata, //数据点
  172. format: function (val, name) { //点击显示的数据注释
  173. return val + '℃';
  174. }
  175. }, {
  176. name: '烟雾浓度',
  177. data: smokesmokedata,
  178. format: function (val, name) {
  179. return val + 'bpm';
  180. }
  181. }, {
  182. name: '湿度',
  183. data: waterwaterdata,
  184. format: function (val, name) {
  185. return val + '%';
  186. }
  187. }
  188. ],
  189. xAxis: { //是否隐藏x轴分割线
  190. disableGrid: true,
  191. },
  192. yAxis: { //y轴数据
  193. title: '数值', //标题
  194. format: function (val) { //返回数值
  195. return val.toFixed(2);
  196. },
  197. min: 30, //最小值
  198. max:180, //最大值
  199. gridColor: '#D8D8D8',
  200. },
  201. width: windowWidth, //图表展示内容宽度
  202. height: windowHeight, //图表展示内容高度
  203. dataLabel: false, //是否在图表上直接显示数据
  204. dataPointShape: true, //是否在图标上显示数据点标志
  205. extra: {
  206. lineStyle: 'curve' //曲线
  207. },
  208. });
  209. },9000)
  210. },
  211. notification: function () {
  212. var _this = this;
  213. var time = _this.data.time;
  214. _this.data.setInter = setInterval(function () {
  215. _this.setData({
  216. time: util.formatTime(new Date())
  217. });
  218. //console.log("时间为"+_this.data.time);
  219. }, 1000);
  220. },
  221. // gettime(){
  222. // var that=this
  223. // var xtime=that.data.xtime
  224. // that.setData({
  225. // xtime:util.formatTime(new Date())
  226. // })
  227. // }
  228. handleItemChange(e){
  229. // console.log(e)
  230. const {index}=e.detail;
  231. let {list}=this.data;
  232. list.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
  233. this.setData({
  234. list
  235. })
  236. }
  237. })

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/413467
推荐阅读
相关标签
  

闽ICP备14008679号