目录
[1 前言](#1 前言)
[2 项目环境](#2 项目环境)
[2.1 硬件准备](#2.1 硬件准备)
[2.2 软件准备](#2.2 软件准备)
[2.3 方案图示](#2.3 方案图示)
[3 应用场景](#3 应用场景)
[3.1 农业大棚环境管理](#3.1 农业大棚环境管理)
[3.2 仓储环境监测](#3.2 仓储环境监测)
[3.3 办公室与公共空间监测](#3.3 办公室与公共空间监测)
[4 例程修改](#4 例程修改)
[4.1 主函数分析](#4.1 主函数分析)
[4.2 ADC采集](#4.2 ADC采集)
[4.3 CO2传感器读取](#4.3 CO2传感器读取)
[4.4 温湿度传感器读取](#4.4 温湿度传感器读取)
[4.5 网页通信处理](#4.5 网页通信处理)
[4.6 网页显示](#4.6 网页显示)
[5 功能验证](#5 功能验证)
[6 总结](#6 总结)
1 前言
HTTP(超文本传输协议,HyperText Transfer Protocol)是一种用于分布式、协作式、超媒体信息系统的应用层协议, 基于 TCP/IP 通信协议来传递数据,是万维网(WWW)的数据通信的基础。设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法,通过 HTTP 或者 HTTPS 协议请求的资源由统一资源标识符(Uniform Resource Identifiers,URI)来标识。 以上是HTTP协议的简介,如想深入了解该协议,请参考mozilla网站上的介绍: HTTP 概述 - HTTP | MDN
W55MH32 是 WIZnet 新推出的高性能以太网单片机。它采用高性能 Arm® Cortex-M3 内核,主频最高达 216MHz,内置 1024KB FLASH、96KB SRAM 。尤为突出的是,其搭载 WIZnet TCP/IP offload 引擎(TOE),集成全硬件 TCP/IP 协议栈、MAC 及 PHY ,还配备 32KB 独立以太网收发缓存,供 8 个硬件 socket 使用,是真正的All-in-One解决方案。
2 项目环境
2.1 硬件准备
- W55MH32L-EVB
- 一根网线
- USB Type-C
- C02传感器
- 光照传感器
2.2 软件准备
- 例程链接:w5500.com/w55mh32.html
- 开发环境:keil uvision 5
- 飞思创串口助手
- 浏览器
2.3 方案图示

3 应用场景
本项目基于 W55MH32 构建的多参数环境监测系统,凭借其低成本、高集成度、Web 可视化等特性,可广泛应用于多个实际场景,满足不同领域的环境感知需求。以下是主要应用场景说明:
3.1 农业大棚环境管理
农业生产中,环境参数直接影响作物生长效率。本系统可部署于温室大棚,实现:
- 实时监测大棚内温湿度(影响作物蒸腾与病害风险)、光照强度(光合作用关键因素)和 CO₂浓度(作物生长碳源);
- 通过 Web 页面远程查看数据,无需现场巡检,降低人工成本;
- 结合历史数据曲线分析环境变化规律,优化灌溉、通风、补光等农事操作策略。
相较于传统农业监测设备,本方案成本更低且支持多参数集成,适合中小规模种植户使用。
3.2 仓储环境监测
对于粮食、药品、电子元件等仓储场景,环境温湿度和空气质量是保障存储品质的关键:
- 监测仓库内温湿度,防止粮食霉变、药品失效或电子元件受潮;
- 实时追踪封闭空间 CO₂浓度,避免因通风不足导致的货物变质;
- 通过历史数据曲线分析环境变化趋势,优化仓储条件与通风周期。
系统支持 24 小时不间断运行,数据稳定性高,适合长期无人值守的仓储场景。
3.3 办公室与公共空间监测
在办公楼、教室、商场等人员密集场所,空气质量与舒适度直接影响人员健康与工作效率:
- 实时监测 CO₂浓度,当人员密集导致浓度升高时,自动联动通风系统改善空气质量;
- 光照强度数据可用于公共区域照明调节,实现节能降耗;
- 管理人员通过浏览器即可远程查看各区域环境状态,集中管控多个监测点(可通过扩展 IP 地址实现多设备部署)。
系统部署简单,只需连接网线即可接入局域网,无需复杂配置。
4 例程修改
4 .1 主函数分析
这段代码实现了一个环境传感器监控系统,通过多种传感器采集温湿度、光照强度和CO2浓度数据,并通过HTTP更新网页显示生成数据曲线。
#include <stdlib.h>`
`#include <string.h>`
`#include <stdio.h>`
`#include "wizchip_conf.h"`
`#include "wiz_interface.h"`
`#include "bsp_tim.h"`
`#include "bsp_uart.h"`
`#include "bsp_rcc.h"`
`#include "delay.h"`
`#include "loopback.h"`
`#include "bsp_adc.h"`
`#include "i2c_sensor.h"`
`#include "uart_sensor.h"`
`#include "light_sensor.h"`
`extern MYI2C_Struct SENx;`
`#define DEFAULT_MAC_EN 1`
`#define SOCKET_ID 0`
`#define ETHERNET_BUF_MAX_SIZE (1024 * 2)`
`float g_humidity_value =` `0.0;`
`float g_temperature =` `0.0;`
`float g_light_intensity =` `0.0;`
`uint16_t g_co2_concentration =` `0;`
`wiz_NetInfo default_net_info =` `{`
`.mac =` `{0x00,` `0x08,` `0xdc,` `0x12,` `0x22,` `0x12},`
`.ip =` `{192,` `168,` `1,` `30},`
`.gw =` `{192,` `168,` `1,` `1},`
`.sn =` `{255,` `255,` `255,` `0},`
`.dns =` `{8,` `8,` `8,` `8},`
`.dhcp = NETINFO_DHCP`
`};`
`uint16_t local_port =` `8080;`
`uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE]` `=` `{0};`
`void` `read_sensors(void)`
`{`
` g_light_intensity =light_read_intensity();`
`// 读取温湿度`
`MYI2C_Handle(&SENx);`
` g_humidity_value = SENx.RH;`
` g_temperature = SENx.T;`
`// 读取CO2浓度`
` g_co2_concentration =` `co2_sensor_get_concentration();`
`printf("Sensor Data - Temp: %.1fC, Humidity: %.1f%%, Light: %.0flux, CO2: %dppm\r\n",`
` g_temperature, g_humidity_value, g_light_intensity, g_co2_concentration);`
`}`
`int` `main(void)`
`{`
`rcc_clk_config();`
`delay_init();`
`console_usart_init(115200);`
`tim3_init();`
`printf("Environmental Sensor Monitoring System\r\n");`
`adc_dma_init();`
`co2_sensor_init();`
`MYI2C_Init(&SENx,` `2000,` `0x38);`
`wiz_toe_init();`
`#if DEFAULT_MAC_EN == 1`
`getSHAR(default_net_info.mac);`
`#endif`
`wiz_phy_link_check();`
`network_init(ethernet_buf,` `&default_net_info);`
`while` `(1)`
`{`
`read_sensors();`
`loopback_tcps(SOCKET_ID, ethernet_buf, local_port);`
`delay_ms(2000);`
`}`
`}`
`
4 .2 ADC采集
通过配置 ADC 为连续扫描模式、DMA 为循环传输模式并关联二者,实现了对 PA3 引脚连接的光传感器数据的高效连续采集,还提供了对 DMA 缓冲区样本求平均值进行滤波,为光照强度计算提供原始数据。
#include "bsp_adc.h"`
`#include "w55mh32_adc.h"`
`#include "w55mh32_dma.h"`
`#include "w55mh32_gpio.h"`
`#include "w55mh32_rcc.h"`
`/* Static buffer for ADC DMA data (stores light sensor readings) */`
`static` `uint16_t s_adc_dma_buffer[ADC_BUFFER_SIZE];`
`/**`
` * @brief Initialize ADC with DMA`
` * @details Configures ADC for light sensor (PA3), GPIO, and DMA for`
` * continuous sampling`
` */`
`void` `adc_dma_init(void)`
`{`
` ADC_InitTypeDef adc_init_struct;`
` GPIO_InitTypeDef gpio_init_struct;`
` DMA_InitTypeDef dma_init_struct;`
`// Enable clocks for ADC, GPIO, and DMA`
`RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);`
`RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);`
`// Configure GPIO pin PA3 as analog input (light sensor)`
` gpio_init_struct.GPIO_Pin = GPIO_Pin_3;`
` gpio_init_struct.GPIO_Mode = GPIO_Mode_AIN;`
`GPIO_Init(GPIOA,` `&gpio_init_struct);`
`// Configure ADC in continuous scan mode`
`ADC_StructInit(&adc_init_struct);`
` adc_init_struct.ADC_Mode = ADC_Mode_Independent;`
` adc_init_struct.ADC_ScanConvMode = ENABLE;`
` adc_init_struct.ADC_ContinuousConvMode = ENABLE;`
` adc_init_struct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;`
` adc_init_struct.ADC_DataAlign = ADC_DataAlign_Right;`
` adc_init_struct.ADC_NbrOfChannel =` `1;` `// Only light sensor channel`
`ADC_Init(ADC1,` `&adc_init_struct);`
`// Configure ADC channel and sampling time (light sensor on PA3)`
`ADC_RegularChannelConfig(ADC1, ADC_Channel_3,` `1, ADC_SampleTime_55Cycles5);`
`// Configure DMA for ADC data transfer`
`DMA_DeInit(DMA1_Channel1);`
` dma_init_struct.DMA_PeripheralBaseAddr =` `(uint32_t)&ADC1->DR;`
` dma_init_struct.DMA_MemoryBaseAddr =` `(uint32_t)s_adc_dma_buffer;`
` dma_init_struct.DMA_DIR = DMA_DIR_PeripheralSRC;`
` dma_init_struct.DMA_BufferSize = ADC_BUFFER_SIZE;`
` dma_init_struct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;`
` dma_init_struct.DMA_MemoryInc = DMA_MemoryInc_Enable;`
` dma_init_struct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;`
` dma_init_struct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;`
` dma_init_struct.DMA_Mode = DMA_Mode_Circular;`
` dma_init_struct.DMA_Priority = DMA_Priority_High;`
` dma_init_struct.DMA_M2M = DMA_M2M_Disable;`
`DMA_Init(DMA1_Channel1,` `&dma_init_struct);`
`// Enable DMA and ADC`
`DMA_Cmd(DMA1_Channel1, ENABLE);`
`ADC_DMACmd(ADC1, ENABLE);`
`// Calibrate and start ADC`
`ADC_Cmd(ADC1, ENABLE);`
`ADC_ResetCalibration(ADC1);`
`while` `(ADC_GetResetCalibrationStatus(ADC1));`
`ADC_StartCalibration(ADC1);`
`while` `(ADC_GetCalibrationStatus(ADC1));`
`ADC_SoftwareStartConvCmd(ADC1, ENABLE);`
`}`
`/**`
` * @brief Get average ADC value for light sensor`
` * @param sample_count Number of samples to average`
` * @return Average ADC value (12-bit)`
` * @note channel_index parameter is now unused (kept for compatibility)`
` */`
`uint16_t` `adc_get_average_value(uint8_t channel_index,` `uint8_t sample_count)`
`{`
`uint32_t sum =` `0;`
`uint8_t valid_samples =` `0;`
`// Ensure we don't exceed buffer size`
`if` `(sample_count > ADC_BUFFER_SIZE)` `{`
` sample_count = ADC_BUFFER_SIZE;`
`}`
`// Sum recent light sensor readings from DMA buffer`
`for` `(uint8_t i =` `0; i < sample_count; i++)` `{`
` sum += s_adc_dma_buffer[i];`
` valid_samples++;`
`}`
`return` `(valid_samples >` `0)` `?` `(sum / valid_samples)` `:` `0;`
`}`
`
4 . 3 CO2传感器读取
实现了 CO₂传感器的驱动功能,通过配置 USART3(波特率 9600bps、8 数据位、1 停止位、无校验)与传感器进行 UART 通信,具体包括初始化 GPIO 和串口硬件、配置中断优先级以处理接收数据。代码中通过co2_sensor_byte_handle函数逐字节接收数据,按照固定格式(6 字节帧,首字节为 0x2C)组装数据帧,再通过co2_sensor_process_data_frame函数验证校验和(B6 为 B1-B5 的 8 位和),若校验通过则将 B2 和 B3 字节组合计算出 CO₂浓度(单位 PPM)并存储到全局变量中,最终通过co2_sensor_get_concentration函数供外部获取,整体实现了传感器数据的可靠接收、校验与解析。
/**`
` * @file co2_sensor.c`
` * @brief CO₂ Sensor drive`
` * `
` * @section uart_config UART Configuration`
` * | Parameter | Value | Description |`
` * |--------------|-------------|------------------------------|`
` * | Baud Rate | 9600bps | Data transmission rate |`
` * | Data Bits | 8 bits | Number of data bits per frame|`
` * | Stop Bit | 1 bit | Stop bit count |`
` * | Parity | None | No parity check |`
` * `
` * @section data_format Data Frame Format`
` * A complete UART data frame consists of 6 bytes (B1 - B6), structured as:`
` * `
` * | Byte Index | Field Name | Fixed Value/Description |`
` * |------------|-------------------|-------------------------------|`
` * | B1 | Module Address | 0x2C (fixed module address) |`
` * | B2 | CO₂ High Byte | 0XXh (high 8 bits of CO₂ data)|`
` * | B3 | CO₂ Low Byte | 0XXh (low 8 bits of CO₂ data) |`
` * | B4 | Full Scale High | 0x03h (fixed value) |`
` * | B5 | Full Scale Low | 0xFFh (fixed value) |`
` * | B6 | Checksum | unit_8 sum of B1-B5 |`
` * `
` * @section checksum_calc Checksum Calculation`
` * Checksum (B6) is computed as: `
` * \f$ B6 = \text{unit\_8}(B1 + B2 + B3 + B4 + B5) \f$ `
` * (Sum B1-B5, then take the 8-bit result)`
` * `
` * @section co2_calc CO₂ Concentration Calculation`
` * CO₂ concentration (in PPM) is calculated by: `
` * \f$ \text{CO₂ (PPM)} = (B2 \times 256) + B3 \f$ `
` * (Combine high/low bytes of CO₂ data into a 16-bit value)`
` * `
` * @section example Example Data`
` * Sample received data frame: 0x2C, 0x01h, 0x90h, 0x03h, 0xFFh, 0xBFh `
` * - Checksum Validation: `
` * \f$ 0xBFh = 0x2C + 0x01h + 0x90h + 0x03h + 0xFFh \f$ (valid) `
` * - CO₂ Concentration: `
` * \f$ (0x01h \times 256) + 0x90h = 256 + 144 = 400 \text{ PPM} \f$ `
` * `
` * @note Ensure data frame length (6 bytes) and module address (B1=0x2C) `
` * are validated before processing.`
` * `
` */`
`#include "w55mh32.h"`
`static` `uint16_t g_co2_concentration =` `0;` `/* Global variable to store current CO2 concentration value */`
`/**`
` * @brief Initialize CO2 sensor`
` * @details Configure USART3 and related GPIO pins, set baud rate to 9600, `
` * 8 data bits, 1 stop bit, no parity`
` */`
`void` `co2_sensor_init(void)`
`{`
` GPIO_InitTypeDef GPIO_InitStructure;`
` USART_InitTypeDef USART_InitStructure;`
` NVIC_InitTypeDef NVIC_InitStructure;`
`/* Enable GPIOB and USART3 clock */`
`RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);`
`RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);`
`/* Configure USART3 TX pin (PB10) as alternate function push-pull */`
` GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;`
` GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;`
` GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;`
`GPIO_Init(GPIOB,` `&GPIO_InitStructure);`
`/* Configure USART3 RX pin (PB11) as input floating */`
` GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;`
` GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;`
`GPIO_Init(GPIOB,` `&GPIO_InitStructure);`
`/* Configure USART3 parameters */`
` USART_InitStructure.USART_BaudRate =` `9600;`
` USART_InitStructure.USART_WordLength = USART_WordLength_8b;`
` USART_InitStructure.USART_StopBits = USART_StopBits_1;`
` USART_InitStructure.USART_Parity = USART_Parity_No;`
` USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;`
` USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;`
`USART_Init(USART3,` `&USART_InitStructure);`
`USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);`
`/* Configure USART3 interrupt priority */`
` NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;`
` NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =` `0;`
` NVIC_InitStructure.NVIC_IRQChannelSubPriority =` `2;`
` NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;`
`NVIC_Init(&NVIC_InitStructure);`
`/* Enable USART3 */`
`USART_Cmd(USART3, ENABLE);`
`}`
`/**`
` * @brief Get current CO2 concentration`
` * @return Current CO2 concentration in PPM`
` */`
`uint16_t` `co2_sensor_get_concentration(void)`
`{`
`return g_co2_concentration;`
`}`
`/**`
` * @brief Process received data frame from CO2 sensor`
` * @param frame Pointer to the 6-byte data frame`
` * @details Validates checksum and updates CO2 concentration if checksum is correct`
` */`
`static` `void` `co2_sensor_process_data_frame(uint8_t` `*frame)`
`{`
`uint8_t i;`
`uint8_t checksum =` `0;`
`/* Calculate checksum */`
`for` `(i =` `0; i <` `5; i++)`
`{`
` checksum += frame[i];`
`}`
`/* Update CO2 concentration if checksum matches */`
`if` `(checksum == frame[5])`
`{`
` g_co2_concentration = frame[1]` `*` `256` `+ frame[2];`
`}`
`}`
`/**`
` * @brief Handle received byte from CO2 sensor`
` * @param data Received byte`
` * @details Assembles complete data frame and processes it when full frame is received`
` */`
`void` `co2_sensor_byte_handle(uint8_t data)`
`{`
`static` `uint8_t co2_data_frame[6];`
`static` `uint8_t co2_data_index =` `0;`
`/* Check for frame start (0x2C) */`
`if` `(co2_data_index ==` `0` `&& data ==` `0x2C)`
`{`
` co2_data_frame[co2_data_index++]` `= data;`
`}`
`/* Continue collecting frame data */`
`else` `if` `(co2_data_index <` `6)`
`{`
` co2_data_frame[co2_data_index++]` `= data;`
`/* Process complete frame */`
`if` `(co2_data_index ==` `6)`
`{`
`co2_sensor_process_data_frame(co2_data_frame);`
` co2_data_index =` `0;`
`}`
`}`
`else`
`{`
` co2_data_index =` `0;`
`}`
`}`
`/**`
` * @brief USART3 interrupt handler`
` * @details Processes received data and clears interrupt flag`
` */`
`void` `USART3_IRQHandler(void)`
`{`
`uint8_t data;`
`if` `(USART_GetITStatus(USART3, USART_IT_RXNE)` `!= RESET)`
`{`
`/* Read received data and process it */`
` data =` `(uint8_t)USART_ReceiveData(USART3);`
`co2_sensor_byte_handle(data);`
`/* Clear interrupt flag */`
`USART_ClearITPendingBit(USART3, USART_IT_RXNE);`
`}`
`}`
`
4 . 4 温湿度传感器读取
该文件实现了 I2C 通信协议的驱动功能,包括初始化 I2C 相关 GPIO 引脚(配置为开漏输出或输入模式)、定义 I2C 起始 / 停止信号、应答 / 等待应答、读写字节等基础通信函数,同时提供了通过 I2C 读写设备寄存器的功能函数,并实现了 CRC8 校验以确保数据传输正确性。代码还包含了针对温湿度传感器的处理逻辑,通过状态机(空闲、测量、完成)管理传感器工作流程,在测量阶段发送指令并读取 7 字节数据,经校验后解析计算出湿度(RH)和温度(T)值,最终通过 MYI2C_Handle 函数完成整个传感器数据的获取与处理过程,用于W55MH32L-EVB板载AHT20温湿度传感器的读取。
#include "i2c_sensor.h "`
`#include "delay.h"`
`MYI2C_Struct SENx;`
`/**********************************************`
`//MYI2C_Delay_us`
`**********************************************/`
`void` `MYI2C_Delay_us(unsigned` `long nTim)`
`{`
`unsigned` `int i;`
`while` `(nTim--)`
`{`
` i = MYI2C_delay_us_cnt;`
`while` `(i--);`
`}`
`}`
`/*******************************************************************************`
`* Function Name : MYI2C_GPIO_MODE`
`* Description : Configures the different GPIO ports.`
`* Input : None`
`* Output : None`
`* Return : None`
`*******************************************************************************/`
`void` `MYI2C_GPIO_MODE(unsigned` `char TYP)` `//根据MCU修改相应的IO初始化`
`{`
`#ifdef ARM32`
` GPIO_InitTypeDef GPIO_InitStruct;`
`#endif`
`switch` `(TYP)`
`{`
`case SDA_OUT:` `//设置开漏输出,需要加外部上拉电阻`
`#ifdef ARM32`
` GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;`
` GPIO_InitStruct.GPIO_Pin = SDA_Pin;`
` GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;`
`GPIO_Init(IIC_SDA_PORT,` `&GPIO_InitStruct);`
`#else`
` P0M0 |=` `1` `<<` `3;`
` P0M1 |=` `3` `<<` `2;`
`#endif`
`break;`
`case SDA_IN:` `//设置输入,需要加外部上拉电阻`
`#ifdef ARM32`
` GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;`
` GPIO_InitStruct.GPIO_Pin = SDA_Pin;`
` GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;`
`GPIO_Init(IIC_SDA_PORT,` `&GPIO_InitStruct);`
`#else`
` P0M0 &=` `~(1` `<<` `3);`
` P0M1 |=` `3` `<<` `2;`
`#endif`
`break;`
`case SCL_OUT:` `//设置开漏输出,需要加外部上拉电阻`
`#ifdef ARM32`
` GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;`
` GPIO_InitStruct.GPIO_Pin = SCL_Pin;`
` GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;`
`GPIO_Init(IIC_SCL_PORT,` `&GPIO_InitStruct);`
`#else`
` P0M0 |=` `1` `<<` `2;`
` P0M1 |=` `3` `<<` `2;`
`#endif`
`break;`
`}`
`}`
`/*******************************************************************************`
`* Function Name : MYI2C_GPIO_DATA`
`* Description : Configures the different GPIO ports.`
`* Input : None`
`* Output : None`
`* Return : None`
`*******************************************************************************/`
`unsigned` `char` `MYI2C_GPIO_DATA(unsigned` `char TYP)` `//根据MCU修改相应的IO操作`
`{`
`unsigned` `int dat =` `0;`
`switch` `(TYP)`
`{`
`case SDA_H:`
`#ifdef ARM32`
`GPIO_SetBits(IIC_SDA_PORT, SDA_Pin);`
`#else`
` I2C_SDA_PIN =` `1;`
`#endif`
`break;`
`case SDA_L:`
`#ifdef ARM32`
`GPIO_ResetBits(IIC_SDA_PORT, SDA_Pin);`
`#else`
` I2C_SDA_PIN =` `0;`
`#endif`
`break;`
`case SCL_H:`
`#ifdef ARM32`
`GPIO_SetBits(IIC_SCL_PORT, SCL_Pin);`
`#else`
` I2C_SCL_PIN =` `1;`
`#endif`
`break;`
`case SCL_L:`
`#ifdef ARM32`
`GPIO_ResetBits(IIC_SCL_PORT, SCL_Pin);`
`#else`
` I2C_SCL_PIN =` `0;`
`#endif`
`break;`
`case SDA_R:`
`#ifdef ARM32`
` dat =` `GPIO_ReadInputDataBit(IIC_SDA_PORT, SDA_Pin);`
`#else`
` dat = I2C_SDA_PIN;`
`#endif`
`break;`
`}`
`return dat;`
`}`
`/**********************************************`
`//IIC Start`
`**********************************************/`
`void` `MYI2C_IIC_Start(void)`
`{`
`MYI2C_SCK_Set();`
`MYI2C_SDA_Set();`
`MYI2C_SDA_Clr();`
`MYI2C_SCK_Clr();`
`}`
`/**********************************************`
`//IIC Stop`
`**********************************************/`
`void` `MYI2C_IIC_Stop(void)`
`{`
`MYI2C_SCK_Clr();`
`MYI2C_SDA_Clr();`
`MYI2C_SCK_Set();`
`MYI2C_SDA_Set();`
`}`
`/**********************************************`
`//IIC Ack`
`**********************************************/`
`void` `MYI2C_IIC_Ack(unsigned` `char ack)`
`{`
`MYI2C_SCK_Clr();`
`if` `(ack)`
`{`
`MYI2C_SDA_Clr();`
`}`
`else`
`{`
`MYI2C_SDA_Set();`
`}`
`MYI2C_SCK_Set();`
`}`
`/**********************************************`
`//IIC Wait_Ack`
`**********************************************/`
`unsigned` `char` `MYI2C_IIC_Wait_Ack(unsigned` `int wait_time)`
`{`
`MYI2C_SCK_Clr();`
` MYI2C_SDA_IN_Mode;`
`MYI2C_SDA_Set();`
`MYI2C_SCK_Set();`
`while` `(wait_time)`
`{`
`if` `(MYI2C_GPIO_DATA(SDA_R)` `==` `0)` `break;`
`MYI2C_Delay_us(1);`
` wait_time--;` `//=======================`
`}`
` MYI2C_SDA_OD_Mode;`
`return wait_time;` `//是否应答=======================`
`}`
`/**********************************************`
`// IIC Write byte`
`**********************************************/`
`void` `MYI2C_Write_IIC_Byte(unsigned` `char dat)`
`{`
`unsigned` `char i;`
`for` `(i =` `0; i <` `8; i++)`
`{`
`MYI2C_SCK_Clr();`
`if` `(dat &` `0x80)`
`{`
`MYI2C_SDA_Set();`
`}`
`else`
`{`
`MYI2C_SDA_Clr();`
`}`
`MYI2C_SCK_Set();`
` dat = dat <<` `1;`
`}`
`}`
`/**********************************************`
`// IIC Read byte`
`**********************************************/`
`unsigned` `char` `MYI2C_Read_IIC_Byte(void)`
`{`
`unsigned` `char i, byt =` `0;`
`MYI2C_SCK_Clr();`
` MYI2C_SDA_IN_Mode;`
`MYI2C_SDA_Set();`
`for` `(i =` `0; i <` `8; i++)`
`{`
`MYI2C_SCK_Clr();`
`if` `(MYI2C_GPIO_DATA(SDA_R)) byt++;`
`MYI2C_SCK_Set();`
`if` `(i <` `7) byt = byt <<` `1;`
`}`
` MYI2C_SDA_OD_Mode;`
`return byt;`
`}`
`/*******************************************************************************`
`* Function Name : MYI2C_Init`
`* Description : 初始化MYI2C`
`* Input : None`
`* Output : None`
`* Return :None`
`*******************************************************************************/`
`void` `MYI2C_Init(MYI2C_Struct *pst,` `unsigned` `int ReadTimMS,` `unsigned` `char xAddr)`
`{`
`RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);` `//使能SDA、SCL端口时钟,如是51单片机不需要则注释掉该行。`
` pst->Adrr = xAddr;`
` pst->Step = SENSOR_IDLE;`
`if` `(ReadTimMS > MinReadTim)`
` pst->SetRTim = ReadTimMS;`
`else`
` pst->SetRTim = MinReadTim;`
` MYI2C_SCK_OD_Mode;`
` MYI2C_SDA_OD_Mode;`
`MYI2C_SCK_Set();`
`MYI2C_SDA_Set();`
`}`
`/*******************************************************************************`
`* Function Name : `
`* Description : `
`* Input :None`
`* Output :None`
`* Return :None`
`*******************************************************************************/`
`unsigned` `char` `MYI2C_READ_FUNC(MYI2C_Struct *pst,` `unsigned` `char device_addr,` `unsigned` `char register_addr,` `unsigned` `char` `*pDat,` `unsigned` `char len)`
`{`
`unsigned` `char NoAck =` `0;`
`if` `(register_addr)`
`{`
`/* Send STRAT condition a second time */`
`MYI2C_IIC_Start();`
`/* Send slave address for Write Regsiter */`
`MYI2C_Write_IIC_Byte((device_addr <<` `1)` `+` `0);`
`/* Ack */`
`if` `(MYI2C_IIC_Wait_Ack(Wait_Ack_time)` `==` `0) NoAck++;`
`/*Send register_addr*/`
`MYI2C_Write_IIC_Byte((register_addr));`
`/* Ack */`
`if` `(MYI2C_IIC_Wait_Ack(Wait_Ack_time)` `==` `0) NoAck++;`
`// MYI2C_IIC_Stop(pst);`
`MYI2C_SCK_Clr();`
`MYI2C_SCK_Set();`
`}`
`/* Send STRAT condition a second time */`
`MYI2C_IIC_Start();`
`/* Send slave address for Read */`
`MYI2C_Write_IIC_Byte((device_addr <<` `1)` `+` `1);`
`/* Ack */`
`if` `(MYI2C_IIC_Wait_Ack(Wait_Ack_time)` `==` `0) NoAck++;`
`/* While there is data to be read */`
`while` `(len && NoAck ==` `0` `&& len < MYI2C_Buffer_Size)` `//`
`{`
`*pDat =` `MYI2C_Read_IIC_Byte();`
`/* Ack */`
`MYI2C_IIC_Ack(len -` `1);` `//len = 1 NoAck`
` pDat++;`
` len--;`
`}`
`/* Send STOP Condition */`
`MYI2C_IIC_Stop();`
` pst->ErrFlag = NoAck;`
`return NoAck;`
`}`
`/*******************************************************************************`
`* Function Name : `
`* Description : `
`* Input : None`
`* Output : None`
`* Return :None`
`*******************************************************************************/`
`unsigned` `char` `MYI2C_WRITE_FUNC(MYI2C_Struct *pst,` `unsigned` `char device_addr,` `unsigned` `char register_addr,` `unsigned` `char` `*pDat,` `unsigned` `char len)`
`{`
`unsigned` `int NoAck =` `0;`
`/* Send STRAT condition */`
`MYI2C_IIC_Start();`
`/* Send slave address for write */`
`MYI2C_Write_IIC_Byte((device_addr <<` `1)` `+` `0);`
`/* ACK */`
`if` `(MYI2C_IIC_Wait_Ack(Wait_Ack_time)` `==` `0) NoAck++;`
`/* Send register_addr for read */`
`MYI2C_Write_IIC_Byte((register_addr));`
`/* ACK */`
`if` `(MYI2C_IIC_Wait_Ack(Wait_Ack_time)` `==` `0) NoAck++;`
`while` `(NoAck ==` `0` `&& len && len < MYI2C_Buffer_Size)` `//`
`{`
`/* Send the byte to be written */`
`MYI2C_Write_IIC_Byte(*pDat);`
`/* Acknowledgement */`
`MYI2C_IIC_Wait_Ack(Wait_Ack_time);`
` pDat++;`
` len--;`
`}`
`/* Send STOP condition */`
`MYI2C_IIC_Stop();`
` pst->ErrFlag = NoAck;`
`return NoAck;`
`}`
`/*******************************************************************************`
`* Function Name : CheckCrc`
`* Description : `
`* Input : None`
`* Output : None`
`* Return :None`
`*******************************************************************************/`
`unsigned` `char` `CheckCrc8(unsigned` `char` `*pDat,` `unsigned` `char Lenth)`
`{`
`unsigned` `char crc =` `0xff, i, j;`
`for` `(i =` `0; i < Lenth; i++)`
`{`
` crc = crc ^` `*pDat;`
`for` `(j =` `0; j <` `8; j++)`
`{`
`if` `(crc &` `0x80)`
` crc =` `(crc <<` `1)` `^` `0x31;`
`else`
` crc <<=` `1;`
`}`
` pDat++;`
`}`
`return crc;`
`}`
`/*******************************************************************************`
`* Function Name : `
`* Description : `
`* Input :None`
`* Output :None`
`* Return :None`
`*******************************************************************************/`
`void` `MYI2C_Handle(MYI2C_Struct *pst)`
`{`
`unsigned` `long s32x;`
` pst->timcnt += MYI2C_Tick;`
`if` `(pst->timcnt > PowerOnTim && pst->Step == SENSOR_IDLE)`
`{`
` pst->Step = SENSOR_MEASURE;`
` pst->SendByte[0]` `=` `0x33;`
` pst->SendByte[1]` `=` `0x00;`
`MYI2C_WRITE_FUNC(pst, pst->Adrr,` `0xAC,` `&pst->SendByte[0],` `2);`
`}`
`else` `if` `(pst->timcnt > MeasureTim && pst->Step == SENSOR_MEASURE)`
`{`
` pst->Step = SENSOR_COMPLETE;`
`MYI2C_READ_FUNC(pst, pst->Adrr,` `0,` `&pst->ReadByte[0],` `7);`
`if` `(pst->ErrFlag ==` `0)`
`{`
`if` `((CheckCrc8(&pst->ReadByte[0],` `6)` `== pst->ReadByte[6])` `&&` `((pst->ReadByte[0]` `&` `0x98)` `==` `0x18))`
`{`
` s32x = pst->ReadByte[1];`
` s32x = s32x <<` `8;`
` s32x += pst->ReadByte[2];`
` s32x = s32x <<` `8;`
` s32x += pst->ReadByte[3];`
` s32x = s32x >>` `4;`
` pst->RH = s32x;`
` pst->RH = pst->RH *` `100` `/` `1048576;`
` s32x = pst->ReadByte[3]` `&` `0x0F;`
` s32x = s32x <<` `8;`
` s32x += pst->ReadByte[4];`
` s32x = s32x <<` `8;`
` s32x += pst->ReadByte[5];`
` pst->T = s32x;`
` pst->T = pst->T *` `200` `/` `1048576` `-` `50;`
`}`
`}`
`else`
`{`
` pst->RH =` `0;`
` pst->T =` `0;`
`}`
`}`
`else` `if` `(pst->timcnt > pst->SetRTim)`
`{`
` pst->Step = SENSOR_IDLE;`
` pst->timcnt =` `0;`
`}`
`}`
`
4 .5 网页通信处理
这段代码实现了基于TCP的HTTP服务器功能,通过状态机管理Socket连接(包括初始化、监听、建立连接、关闭等状态处理),解析HTTP请求行获取方法、URI和版本信息,针对不同请求做出响应:对OPTIONS请求返回204无内容,对根路径GET请求发送网页内容,对`/api/sensor`路径GET请求以JSON格式返回温度、湿度、光照强度和CO₂浓度等传感器数据,对无效请求返回400或404错误,同时处理数据发送和连接断开逻辑,实现传感器数据的网络访问功能。
cpp
#include "loopback.h"
#include "socket.h"
#include "web_page.h"
#include "wizchip_conf.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DATA_BUF_SIZE 1024
extern float g_humidity_value;
extern float g_temperature;
extern float g_light_intensity;
extern uint16_t g_co2_concentration;
typedef struct {
char method[16];
char uri[256];
char version[16];
} HttpReqLine;
static int http_parse_request_line(const char *request, HttpReqLine *req_line)
{
char buffer[1024];
strncpy(buffer, request, sizeof(buffer));
buffer[sizeof(buffer) - 1] = '\0';
char *line_end = strstr(buffer, "\r\n");
if (!line_end) line_end = strstr(buffer, "\n");
if (!line_end) return -1;
*line_end = '\0';
char *method = strtok(buffer, " ");
char *uri = strtok(NULL, " ");
char *version = strtok(NULL, " ");
if (!method || !uri || !version) return -1;
strncpy(req_line->method, method, sizeof(req_line->method) - 1);
strncpy(req_line->uri, uri, sizeof(req_line->uri) - 1);
strncpy(req_line->version, version, sizeof(req_line->version) - 1);
return 0;
}
static void send_http_response(uint8_t sn, const char *status,
const char *content_type, const char *body)
{
char response[512];
int body_len = strlen(body);
sprintf(response,
"HTTP/1.1 %s\r\n"
"Content-Type: %s\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Connection: close\r\n"
"Content-Length: %d\r\n"
"\r\n"
"%s",
status, content_type, body_len, body);
send(sn, (uint8_t *)response, strlen(response));
}
int32_t loopback_tcps(uint8_t sn, uint8_t *network_buf, uint16_t port)
{
int32_t ret;
uint16_t recv_size = 0;
uint16_t send_size = 0;
switch (getSn_SR(sn)) {
case SOCK_ESTABLISHED:
if (getSn_IR(sn) & Sn_IR_CON) {
setSn_IR(sn, Sn_IR_CON);
}
if ((recv_size = getSn_RX_RSR(sn)) > 0) {
if (recv_size > DATA_BUF_SIZE) recv_size = DATA_BUF_SIZE;
ret = recv(sn, network_buf, recv_size);
if (ret <= 0) return ret;
recv_size = (uint16_t)ret;
network_buf[recv_size] = '\0';
HttpReqLine req_line;
if (http_parse_request_line((char *)network_buf, &req_line) == 0) {
printf("HTTP %s: %s\n", req_line.method, req_line.uri);
if (strcmp(req_line.method, "OPTIONS") == 0) {
send_http_response(sn, "204 No Content", "text/plain", "");
}
else if (strcmp(req_line.method, "GET") == 0 && strcmp(req_line.uri, "/") == 0) {
// 直接发送 index_page
uint16_t content_len = strlen(index_page);
send_size = 0;
while (send_size < content_len) {
ret = send(sn, (uint8_t *)index_page + send_size, content_len - send_size);
if (ret < 0) {
close(sn);
return ret;
}
send_size += ret;
}
}
else if (strcmp(req_line.method, "GET") == 0 && strcmp(req_line.uri, "/api/sensor") == 0) {
char sensor_json[256];
sprintf(sensor_json,
"{\"temp\":%.1f,\"humi\":%.1f,\"light\":%.1f,\"co2\":%d}",
g_temperature, g_humidity_value, g_light_intensity, g_co2_concentration);
send_http_response(sn, "200 OK", "application/json", sensor_json);
}
else {
send_http_response(sn, "404 Not Found", "text/html",
"<html><body>Page Not Found</body></html>");
}
}
else {
send_http_response(sn, "400 Bad Request", "text/plain", "Invalid request");
}
disconnect(sn);
close(sn);
}
break;
case SOCK_CLOSE_WAIT:
disconnect(sn);
break;
case SOCK_INIT:
listen(sn);
break;
case SOCK_CLOSED:
socket(sn, Sn_MR_TCP, port, 0x00);
break;
default:
break;
}
return 1;
}
4 .6 网页显示
这段代码定义了一个环境传感器仪表盘的HTML页面,通过引入Chart.js实现温度、湿度、光照强度和二氧化碳浓度的实时数值显示与历史数据折线图展示,支持手动刷新和5秒自动刷新,通过请求`/api/sensor`接口获取数据并动态更新页面内容,整体采用网格布局和响应式设计提升视觉体验。
cpp
#ifndef _WEBP_PAGE_H_
#define _WEBP_PAGE_H_
#define index_page \
"HTTP/1.1 200 OK\r\n" \
"Content-Type: text/html\r\n" \
"Connection: close\r\n" \
"\r\n" \
"<!DOCTYPE html>\r\n" \
"<html>\r\n" \
"<head>\r\n" \
" <meta charset=\"UTF-8\">\r\n" \
" <title>传感器仪表盘</title>\r\n" \
" <script src=\"https://cdn.jsdelivr.net/npm/chart.js\"></script>\r\n" \
" <style>\r\n" \
" body { font-family: \"Microsoft YaHei\", \"微软雅黑\", sans-serif; max-width: 1000px; margin: 0 auto; padding: 20px; }\r\n" \
" .dashboard { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }\r\n" \
" .sensor-panel { background: #f8f9fa; padding: 15px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }\r\n" \
" .chart-container { height: 200px; margin-top: 15px; }\r\n" \
" .refresh-btn { background: #4CAF50; color: white; border: none; padding: 10px 15px; border-radius: 4px; cursor: pointer; width: 100%; margin: 10px 0; font-size: 16px; }\r\n" \
" .value-display { font-size: 24px; font-weight: bold; text-align: center; margin: 10px 0; }\r\n" \
" h1, h2 { color: #2c3e50; }\r\n" \
" </style>\r\n" \
"</head>\r\n" \
"<body>\r\n" \
" <h1 style=\"text-align: center;\">环境传感器仪表盘</h1>\r\n" \
" <button id=\"refresh_button\" class=\"refresh-btn\">刷新数据</button>\r\n" \
" \r\n" \
" <div class=\"dashboard\">\r\n" \
" <div class=\"sensor-panel\">\r\n" \
" <h2>温度</h2>\r\n" \
" <div id=\"temp_value\" class=\"value-display\">-- °C</div>\r\n" \
" <div class=\"chart-container\">\r\n" \
" <canvas id=\"temp_chart\"></canvas>\r\n" \
" </div>\r\n" \
" </div>\r\n" \
" \r\n" \
" <div class=\"sensor-panel\">\r\n" \
" <h2>湿度</h2>\r\n" \
" <div id=\"humi_value\" class=\"value-display\">-- %</div>\r\n" \
" <div class=\"chart-container\">\r\n" \
" <canvas id=\"humi_chart\"></canvas>\r\n" \
" </div>\r\n" \
" </div>\r\n" \
" \r\n" \
" <div class=\"sensor-panel\">\r\n" \
" <h2>光照强度</h2>\r\n" \
" <div id=\"light_value\" class=\"value-display\">-- lux</div>\r\n" \
" <div class=\"chart-container\">\r\n" \
" <canvas id=\"light_chart\"></canvas>\r\n" \
" </div>\r\n" \
" </div>\r\n" \
" \r\n" \
" <div class=\"sensor-panel\">\r\n" \
" <h2>二氧化碳浓度</h2>\r\n" \
" <div id=\"co2_value\" class=\"value-display\">-- ppm</div>\r\n" \
" <div class=\"chart-container\">\r\n" \
" <canvas id=\"co2_chart\"></canvas>\r\n" \
" </div>\r\n" \
" </div>\r\n" \
" </div>\r\n" \
" \r\n" \
" <script>\r\n" \
" // 初始化变量和DOM元素\r\n" \
" const sensors = {\r\n" \
" temp: { element: document.getElementById('temp_value'), chart: null, values: [] },\r\n" \
" humi: { element: document.getElementById('humi_value'), chart: null, values: [] },\r\n" \
" light: { element: document.getElementById('light_value'), chart: null, values: [] },\r\n" \
" co2: { element: document.getElementById('co2_value'), chart: null, values: [] }\r\n" \
" };\r\n" \
" \r\n" \
" const refreshBtn = document.getElementById('refresh_button');\r\n" \
" const url = '/api/sensor';\r\n" \
" \r\n" \
" // 初始化图表\r\n" \
" function initCharts() {\r\n" \
" // 传感器中文名称映射\r\n" \
" const sensorNames = {\r\n" \
" temp: '温度',\r\n" \
" humi: '湿度',\r\n" \
" light: '光照',\r\n" \
" co2: '二氧化碳'\r\n" \
" };\r\n" \
" \r\n" \
" Object.keys(sensors).forEach(sensor => {\r\n" \
" const ctx = document.getElementById(sensor + '_chart').getContext('2d');\r\n" \
" sensors[sensor].chart = new Chart(ctx, {\r\n" \
" type: 'line',\r\n" \
" data: {\r\n" \
" labels: [],\r\n" \
" datasets: [{\r\n" \
" label: sensorNames[sensor] + '历史数据',\r\n" \
" data: [],\r\n" \
" borderColor: getColor(sensor),\r\n" \
" backgroundColor: getColor(sensor, 0.2),\r\n" \
" tension: 0.4,\r\n" \
" fill: true\r\n" \
" }]\r\n" \
" },\r\n" \
" options: {\r\n" \
" responsive: true,\r\n" \
" maintainAspectRatio: false,\r\n" \
" scales: {\r\n" \
" y: { \r\n" \
" beginAtZero: sensor !== 'temp',\r\n" \
" title: {\r\n" \
" display: true,\r\n" \
" text: sensor === 'temp' ? '温度(°C)' : \r\n" \
" sensor === 'humi' ? '湿度(%)' : \r\n" \
" sensor === 'light' ? '光照(lux)' : '浓度(ppm)'\r\n" \
" }\r\n" \
" },\r\n" \
" x: {\r\n" \
" title: {\r\n" \
" display: true,\r\n" \
" text: '时间'\r\n" \
" }\r\n" \
" }\r\n" \
" },\r\n" \
" plugins: {\r\n" \
" legend: {\r\n" \
" labels: {\r\n" \
" font: {\r\n" \
" family: 'Microsoft YaHei',\r\n" \
" size: 12\r\n" \
" }\r\n" \
" }\r\n" \
" }\r\n" \
" }\r\n" \
" }\r\n" \
" });\r\n" \
" });\r\n" \
" }\r\n" \
" \r\n" \
" // 获取传感器对应的颜色\r\n" \
" function getColor(sensor, opacity = 1) {\r\n" \
" const colors = {\r\n" \
" temp: `rgba(219, 68, 55, ${opacity})`,\r\n" \
" humi: `rgba(66, 133, 244, ${opacity})`,\r\n" \
" light: `rgba(244, 180, 0, ${opacity})`,\r\n" \
" co2: `rgba(15, 157, 88, ${opacity})`\r\n" \
" };\r\n" \
" return colors[sensor] || `rgba(100, 100, 100, ${opacity})`;\r\n" \
" }\r\n" \
" \r\n" \
" // 更新传感器显示和图表\r\n" \
" function updateSensorData(data) {\r\n" \
" // 更新数值显示\r\n" \
" sensors.temp.element.textContent = data.temp.toFixed(1) + ' °C';\r\n" \
" sensors.humi.element.textContent = data.humi.toFixed(1) + ' %';\r\n" \
" sensors.light.element.textContent = Math.round(data.light) + ' lux';\r\n" \
" sensors.co2.element.textContent = data.co2 + ' ppm';\r\n" \
" \r\n" \
" // 更新图表\r\n" \
" const now = new Date();\r\n" \
" const timeLabel = now.getHours() + ':' + now.getMinutes().toString().padStart(2, '0');\r\n" \
" \r\n" \
" Object.keys(sensors).forEach(sensor => {\r\n" \
" const chart = sensors[sensor].chart;\r\n" \
" const values = sensors[sensor].values;\r\n" \
" \r\n" \
" values.push(data[sensor]);\r\n" \
" if (values.length > 20) values.shift();\r\n" \
" \r\n" \
" chart.data.labels.push(timeLabel);\r\n" \
" if (chart.data.labels.length > 20) chart.data.labels.shift();\r\n" \
" \r\n" \
" chart.data.datasets[0].data = [...values];\r\n" \
" chart.update();\r\n" \
" });\r\n" \
" }\r\n" \
" \r\n" \
" // 从服务器获取传感器数据\r\n" \
" function fetchSensorData() {\r\n" \
" fetch(url)\r\n" \
" .then(response => {\r\n" \
" if (!response.ok) {\r\n" \
" throw new Error('网络响应异常');\r\n" \
" }\r\n" \
" return response.json();\r\n" \
" })\r\n" \
" .then(data => {\r\n" \
" updateSensorData(data);\r\n" \
" })\r\n" \
" .catch(error => {\r\n" \
" console.error('获取传感器数据失败:', error);\r\n" \
" // 显示错误信息\r\n" \
" Object.keys(sensors).forEach(sensor => {\r\n" \
" });\r\n" \
" });\r\n" \
" }\r\n" \
" \r\n" \
" // 事件监听\r\n" \
" refreshBtn.addEventListener('click', fetchSensorData);\r\n" \
" \r\n" \
" // 初始化并开始定期更新\r\n" \
" initCharts();\r\n" \
" fetchSensorData();\r\n" \
" setInterval(fetchSensorData, 5000);\r\n" \
" </script>\r\n" \
"</body>\r\n" \
"</html>"
#endif
5 功能验证
- 硬件连接完毕,烧录程序上电打印如下信息:PHY配置打印网络信息。

- 在浏览器输入19168.2.49:8080来进入html页面,可以看到读取传感器数据变化曲线。

6 总结
本项目基于 W55MH32 以太网单片机构建的多传感器环境监测系统,成功实现了温湿度、光照、CO₂等环境参数的实时采集、Web 页面可视化及数据动态更新。通过硬件 TCP/IP 协议栈与 HTTP 服务器的结合,验证了嵌入式设备直接构建网络监测平台的可行性,为低成本环境感知方案提供了可靠参考。感谢大家的耐心阅读!如果您在阅读过程中有任何疑问,或者希望进一步了解这款产品及其应用,欢迎随时通过私信或评论区留言。我们会尽快回复您的消息,为您提供更详细的解答和帮助!