目录

摘要

一、系统方案

(一)系统功能

(二)系统结构

(三)元件选择

1.主控处理器的选择

2.从控处理器的选择

3.温湿度检测模块的选择

4.ADC采集模块的选择

5.显示模块的选择

二、电路设计

(一)电路原理图

(二)实物接线图

三、程序设计

(一)STM32程序设计

1.时钟模块

2.温湿度采集模块

3.延时模块

4.定时器模块 

5.OLED显示模块

6.I2C模块

7.ADC采集模块

8.USART模块

9.调试模块

10.操作系统模块

11.FreeRTOS模块

12.DMA开启和硬件检测模块

13.注意事项

(二)ESP32程序设计

1.程序代码

2.代码说明

四、测试结果

(一)STM32测试

1.OLED打印

2.串口发送数据

(二)ESP32测试

1.上传数据

2.云端显示

五、参考文章 


摘要

       基于 STM32 + ESP32 开发的环境检测系统,可以实时检测环境温湿度、光照等参数,数据可以通过 OLED 实现本地显示,并支持无线传输功能,可以在云平台 OneNet 上实时显示数据,远距离监视环境的变化。

一、系统方案

(一)系统功能

(二)系统结构

        通过电脑+ST-LINK以及电脑+USB线分别对STM32、ESP32进行供电、烧录和调试,由STM32负责控制,DHT11温湿度传感器负责检测温湿度,ADC负责收集光照等数据,OLED显示屏负责本地打印数据,串口负责将数据发送给ESP32,ESP32将串口接收的数据通过WIFI模块发送到云平台OneNet。

(三)元件选择

1.主控处理器的选择

STM32F103C8T6

        STM32凭借低成本优势及成熟的开发生态,在嵌入式系统开发中占据核心地位。ST 官方提供的 HAL 库与标准外设库,通过标准化 API 大幅简化了 GPIO、定时器、通信(UART/SPI/I2C 等)的配置流程,降低开发门槛。该微控制器集成 12 位 ADC、多通道定时器、PWM输出等丰富外设,可无缝对接温湿度传感器、显示模块、电机驱动等设备,覆盖工业控制、智能家居、仪器仪表等多元应用场景。

2.从控处理器的选择

ESP32

        ESP32 因其高集成度、低功耗和强大的无线通信能力,在物联网开发领域得到了广泛应用。丰富的开发框架(如 ESP-IDF、Arduino 适配层)使得其编程实现简单高效。该微控制器集成了 Wi-Fi、蓝牙双模通信模块,以及 GPIO、ADC、DAC、UART、SPI、I2C 等多种外设,可轻松对接传感器、显示屏、执行器等外围设备,适用于智能家居、可穿戴设备、工业监测等多种应用场景。由于 ESP32 系列在物联网领域的普及,网络上有海量的技术文档、开源项目和活跃社区,开发者遇到问题时能快速获取解决方案。

3.温湿度检测模块的选择

DHT11

          DHT11 是低成本数字温湿度传感器,采用单总线通信,接线极简(仅 3 引脚)。可测 0~50℃(±2℃)、20%~90% RH(±5% RH),适合普通环境监测。

4.ADC采集模块的选择

光敏电阻模块

        模拟型光照模块以光敏电阻为核心,利用光电导效应(光照越强电阻越小)将光信号转为模拟电压,需接控制器 ADC 引脚读取。成本低、精度一般,适合楼道照明等简易场景。  

热敏电阻模块

        模拟型温度模块以热敏电阻为核心,利用热电阻效应(温度越高电阻越小)将温度信号转为模拟电压,需接控制器 ADC 引脚读取。成本低、精度一般,适合室内温度检测等简易场景。

5.显示模块的选择

0.96寸OLED

        OLED自发光、高对比度、功耗低,适合小尺寸显示。常用I2C或SPI接口,接线简单,适配主流控制器。支持字符、图形显示,广泛用于便携设备、传感器数据展示等场景。

二、电路设计

(一)电路原理图

(二)实物接线图

 

三、程序设计

(一)STM32程序设计

1.时钟模块

2.温湿度采集模块

  dht11.c

void DHT11_OUT(void )//输出模式
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin = GPIO_PIN_8;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

void DHT11_IN(void )//输入模式
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin = GPIO_PIN_8;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT ;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

void DHT11_REST(void ) //主机发起初始信号
{
	DHT11_OUT();
	HAL_GPIO_WritePin (GPIOA, GPIO_PIN_8, GPIO_PIN_RESET );
	delay_ms (20);
	HAL_GPIO_WritePin (GPIOA, GPIO_PIN_8, GPIO_PIN_SET );
	delay_us (30);
}

uint8_t DHT11_Check(void)
{
    uint8_t retry = 0;

    DHT11_IN();  // 设置数据引脚为输入模式

    while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8) == 1 && retry < 80)
    {
        retry++;
        delay_us(1);  // 延迟1微秒
    }

    if (retry >= 80)
        return 1;  // 如果在规定时间内仍为高电平,表示传感器不响应 返回错误代码1
    else
        retry = 0;

    while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8) == 0 && retry < 80)
    {
        retry++;
        delay_us(1);  
    }

    if (retry >= 80)
        return 1;  // 如果在规定时间内仍为高电平,表示传感器不响应 返回错误代码1

    return 0;  // 传感器响应正常 返回正确代码0
}

//读取一个位
uint8_t DHT11_Read_Bit(void)
{
	uint8_t retry=0;
	while((HAL_GPIO_ReadPin (GPIOA ,GPIO_PIN_8) == 1 ) && (retry < 100))
	{
		retry ++;
		delay_us (1);
	}
	retry=0;
	while((HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8) == 0) && (retry < 100))
	{
		retry ++;
		delay_us (1);
	}
	delay_us (40);
	if(HAL_GPIO_ReadPin (GPIOA, GPIO_PIN_8) == 1)
		return 1;  // 返回读取到的位为高电平
	else 
		return 0;  // 返回读取到的位为低电平
}

//读取一个字节
uint8_t DHT11_Read_Byte(void)
{
	uint8_t dat=0;
	for(uint8_t i=0;i<8;i++)
	{
		dat <<= 1;
		dat |= DHT11_Read_Bit();
	}
	return dat;

}

//更改数据格式,解码数据
uint8_t DHT11_Read_Data(uint8_t* humi, uint8_t* temp)
{
    uint8_t buf[5];  // 储存读取到的五个字节数据到缓冲区

    DHT11_REST();  // 初始化传感器通信

    if (DHT11_Check() == 0)  // 检查传感器是否正常响应
    {
        for (uint8_t i = 0; i < 5; i++)
            buf[i] = DHT11_Read_Byte();  // 逐个字节读取数据

        if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])  // 检校验数据的正确性
        {
            *humi = buf[0];  // 温度值
            *temp = buf[2];  // 湿度值
        }
    }
    else
    {
        return 1;  // 传感器响应异常,返回错误代码
    }

    return 0;  // 读取数据正确,返回正常代码
}

注意:温度模块需要配合延时模块使用 !!!

3.延时模块

  delay.c

void delay_us(uint16_t nus)
{
	__HAL_TIM_SET_COUNTER(&htim1, 0);
	__HAL_TIM_ENABLE(&htim1);
	while (__HAL_TIM_GET_COUNTER(&htim1) < nus)
	{
	}
	__HAL_TIM_DISABLE(&htim1);
}

void delay_ms(uint32_t nms) 
{
    // 分块处理,每次最多处延时65ms(避免16位计数器溢出)
    while (nms > 65) {
        delay_us(65000);  // 延时65ms
        nms -= 65;
    }
    // 处理剩余毫秒数
    delay_us(nms * 1000);
}

注意:延时模块需要配合定时器使用 !!!

4.定时器模块 

TIM1的配置 

 

 

5.OLED显示模块

  oled.c

/**********************************************************
 * 初始化命令,根据芯片手册书写,详细步骤见上图以及注意事项
 ***********************************************************/
uint8_t CMD_Data[]={
0xAE, 0xD5, 0x80, 0xA8, 0x3F, 0xD3, 0x00, 0x40,0xA1, 0xC8, 0xDA,

0x12, 0x81, 0xCF, 0xD9, 0xF1, 0xDB, 0x40, 0xA4, 0xA6,0x8D, 0x14,

0xAF};



/**
 * @function: void OLED_Init(void)
 * @description: OLED初始化
 * @return {*}
 */
void OLED_Init(void)
{
	HAL_Delay(200);

	uint8_t i = 0;
	for(i=0; i<23; i++)
	{
		OLED_WR_CMD(CMD_Data[i]);
	}
	
}

/**
 * @function: void OLED_WR_CMD(uint8_t cmd)
 * @description: 向设备写控制命令
 * @param {uint8_t} cmd 芯片手册规定的命令
 * @return {*}
 */
void OLED_WR_CMD(uint8_t cmd)
{
	HAL_I2C_Mem_Write(&hi2c1 ,0x78,0x00,I2C_MEMADD_SIZE_8BIT,&cmd,1,0x100);
}

/**
 * @function: void OLED_WR_DATA(uint8_t data)
 * @description: 向设备写控制数据
 * @param {uint8_t} data 数据
 * @return {*}
 */
void OLED_WR_DATA(uint8_t data)
{
	HAL_I2C_Mem_Write(&hi2c1 ,0x78,0x40,I2C_MEMADD_SIZE_8BIT,&data,1,0x100);
}

/**
 * @function: void OLED_On(void)
 * @description: 更新显示

 * @return {*}
 */
void OLED_On(void)
{
	uint8_t i,n;
	for(i=0;i<8;i++)
	{
		OLED_WR_CMD(0xb0+i);    //设置页地址(0~7)
		OLED_WR_CMD(0x00);      //设置显示位置—列低地址
		OLED_WR_CMD(0x10);      //设置显示位置—列高地址
		for(n=0;n<128;n++)
			OLED_WR_DATA(1);
	}
}


/**
 * @function: OLED_Clear(void)
 * @description: 清屏,整个屏幕是黑色的!和没点亮一样!!!
 * @return {*}
 */
void OLED_Clear(void)
{
	uint8_t i,n;
	for(i=0;i<8;i++)
	{
		OLED_WR_CMD(0xb0+i);    //设置页地址(0~7)
		OLED_WR_CMD(0x00);      //设置显示位置—列低地址
		OLED_WR_CMD(0x10);      //设置显示位置—列高地址
		for(n=0;n<128;n++)
			OLED_WR_DATA(0);
	}
}

/**
 * @function: void OLED_Display_On(void)
 * @description: 开启OLED显示
 * @return {*}
 */
void OLED_Display_On(void)
{
	OLED_WR_CMD(0X8D);  //SET DCDC命令
	OLED_WR_CMD(0X14);  //DCDC ON
	OLED_WR_CMD(0XAF);  //DISPLAY ON,打开显示
}


/**
 * @function: void OLED_Display_Off(void)
 * @description: 关闭OLED显示
 * @return {*}
 */
void OLED_Display_Off(void)
{
	OLED_WR_CMD(0X8D);  //SET DCDC命令
	OLED_WR_CMD(0X10);  //DCDC OFF
	OLED_WR_CMD(0XAE);  //DISPLAY OFF,关闭显示
}

/**
 * @function: void OLED_Set_Pos(uint8_t x, uint8_t y)
 * @description: 坐标设置
 * @param {uint8_t} x,y
 * @return {*}
 */
void OLED_Set_Pos(uint8_t x, uint8_t y)
{
	OLED_WR_CMD(0xb0+y);	//设置页地址(0~7)
	OLED_WR_CMD(((x&0xf0)>>4)|0x10); //设置显示位置—列高地址
	OLED_WR_CMD(x&0x0f);	//设置显示位置—列低地址
}


/**
 * @function: unsigned int oled_pow(uint8_t m,uint8_t n)
 * @description: m^n函数
 * @param {uint8_t} m,n
 * @return {unsigned int} result
 */
unsigned int oled_pow(uint8_t m,uint8_t n)
{
	unsigned int result=1;
	while(n--)result*=m;
	return result;
}

/**
 * @function: void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t Char_Size,uint8_t Color_Turn)
 * @description: 在OLED12864特定位置开始显示一个字符
 * @param {uint8_t} x字符开始显示的横坐标
 * @param {uint8_t} y字符开始显示的纵坐标
 * @param {uint8_t} chr待显示的字符
 * @param {uint8_t} Char_Size待显示字符的字体大小,选择字体 16/12
 * @param {uint8_t} Color_Turn是否反相显示(1反相、0不反相)
 * @return {*}
 */
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t Char_Size,uint8_t Color_Turn)
{
	unsigned char c=0,i=0;
		c=chr-' ';//得到偏移后的值
		if(x>128-1){x=0;y=y+2;}
		if(Char_Size ==16)
		{
			OLED_Set_Pos(x,y);
			for(i=0;i<8;i++)
				{
				  if(Color_Turn)
					  OLED_WR_DATA(~F8X16[c*16+i]);
				  else
					  OLED_WR_DATA(F8X16[c*16+i]);
				}
			OLED_Set_Pos(x,y+1);
			for(i=0;i<8;i++)
			    {
				  if(Color_Turn)
					  OLED_WR_DATA(~F8X16[c*16+i+8]);
				  else
					  OLED_WR_DATA(F8X16[c*16+i+8]);
			    }

			}
	     else
	     {
				OLED_Set_Pos(x,y);
				for(i=0;i<6;i++)
			    {
				  if(Color_Turn)
					  OLED_WR_DATA(~F6x8[c][i]);
				  else
					  OLED_WR_DATA(F6x8[c][i]);
			    }
		  }
}

/**
 * @function: void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *chr, uint8_tChar_Size, uint8_t Color_Turn)
 * @description: 在OLED12864特定位置开始显示字符串
 * @param {uint8_t} x待显示字符串的开始横坐标x:0~127
 * @param {uint8_t} y待显示字符串的开始纵坐标 y:0~7,若选择字体大小为16,则两行数字之间需要间隔2,若选择字体大小为12,间隔1
 * @param {uint8_t} *chr待显示的字符串
 * @param {uint8_t} Char_Size待显示字符串的字体大小,选择字体 16/12,16为8X16,12为6x8
 * @param {uint8_t} Color_Turn是否反相显示(1反相、0不反相)
 * @return {*}
 */
void OLED_ShowString(uint8_t x,uint8_t y,char*chr,uint8_t Char_Size, uint8_t Color_Turn)
{
	uint8_t  j=0;
	while (chr[j]!='\0')
	{		OLED_ShowChar(x,y,chr[j],Char_Size, Color_Turn);
			if (Char_Size == 12) //6X8的字体列加6,显示下一个字符
				x += 6;
			else  //8X16的字体列加8,显示下一个字符
				x += 8;

			if (x > 122 && Char_Size==12) //TextSize6x8如果一行不够显示了,从下一行继续显示
			{
				x = 0;
				y++;
			}
			if (x > 120 && Char_Size== 16) //TextSize8x16如果一行不够显示了,从下一行继续显示
			{
				x = 0;
				y++;
			}
			j++;
	}
}

/**
 * @function: void OLED_ShowNum(uint8_t x,uint8_t y,unsigned int num,uint8_t len,uint8_t size2, Color_Turn)
 * @description: 显示数字
 * @param {uint8_t} x待显示的数字起始横坐标,x:0~126
 * @param {uint8_t} y待显示的数字起始纵坐标, y:0~7,若选择字体大小为16,则两行数字之间需要间隔2,若选择字体大小为12,间隔1
 * @param {unsigned int} num:输入的数据
 * @param {uint8_t } len:输入的数据位数
 * @param {uint8_t} size2:输入的数据大小,选择 16/12,16为8X16,12为6x8
 * @param {uint8_t} Color_Turn是否反相显示(1反相、0不反相)
 * @return {*}
 */
void OLED_ShowNum(uint8_t x,uint8_t y,unsigned int num,uint8_t len,uint8_t size2, uint8_t Color_Turn)
{
	uint8_t t,temp;
	uint8_t enshow=0;
	for(t=0;t<len;t++)
	{
		temp=(num/oled_pow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				OLED_ShowChar(x+(size2/2)*t,y,' ',size2, Color_Turn);
				continue;
			}else enshow=1;

		}
	 	OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2, Color_Turn);
	}
}


/**
 * @function: void OLED_Showdecimal(uint8_t x,uint8_t y,float num,uint8_t z_len,uint8_t f_len,uint8_t size2, uint8_t Color_Turn)
 * @description: 显示正负浮点数
 * @param {uint8_t} x待显示的数字起始横坐标,x:0~126
 * @param {uint8_t} y待显示的数字起始纵坐标, y:0~7,若选择字体大小为16,则两行数字之间需要间隔2,若选择字体大小为12,间隔1
 * @param {float} num:输入的浮点型数据
 * @param {uint8_t } z_ len:整数部分的位数
 * @param {uint8_t } f_len: 小数部分的位数
 * @param {uint8_t} size2:输入的数据大小,选择 16/12,16为8X16,12为6x8
 * @param {uint8_t} Color_Turn是否反相显示(1反相、0不反相)
 * @return {*}
 */
void OLED_Showdecimal(uint8_t x,uint8_t y,float num,uint8_t z_len,uint8_t f_len,uint8_t size2, uint8_t Color_Turn)
{
	uint8_t t,temp,i=0;//i为负数标志位
	uint8_t enshow;
	int z_temp,f_temp;
	if(num<0)
	{
		z_len+=1;
		i=1;
		num=-num;
	}
	z_temp=(int)num;
	//整数部分
	for(t=0;t<z_len;t++)
	{
		temp=(z_temp/oled_pow(10,z_len-t-1))%10;
		if(enshow==0 && t<(z_len-1))
		{
			if(temp==0)
			{
				OLED_ShowChar(x+(size2/2)*t,y,' ',size2, Color_Turn);
				continue;
			}
			else
			enshow=1;
		}
		OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2, Color_Turn);
	}
	//小数点
	OLED_ShowChar(x+(size2/2)*(z_len),y,'.',size2, Color_Turn);

	f_temp=(int)((num-z_temp)*(oled_pow(10,f_len)));
  //小数部分
	for(t=0;t<f_len;t++)
	{
		temp=(f_temp/oled_pow(10,f_len-t-1))%10;
		OLED_ShowChar(x+(size2/2)*(t+z_len)+5,y,temp+'0',size2, Color_Turn);
	}
	if(i==1)//如果为负,就将最前的一位赋值‘-’
	{
		OLED_ShowChar(x,y,'-',size2, Color_Turn);
		i=0;
	}
}



/**
 * @function: void OLED_ShowCHinese(uint8_t x,uint8_t y,uint8_t no, uint8_t Color_Turn)
 * @description: 在OLED特定位置开始显示16X16汉字
 * @param {uint8_t} x待显示的汉字起始横坐标x: 0~112,两列汉字之间需要间隔16
 * @param {uint8_t} y待显示的汉字起始纵坐标 y: 0~6 , 两行汉字之间需要间隔2
 * @param {uint8_t} no待显示的汉字编号
 * @param {uint8_t} Color_Turn是否反相显示(1反相、0不反相)
 * @return {*}
 */
void OLED_ShowCHinese(uint8_t x,uint8_t y,uint8_t no, uint8_t Color_Turn)
{
	uint8_t t=0;
	OLED_Set_Pos(x,y);
    for(t=0;t<16;t++)
		{
				if (Color_Turn)
					OLED_WR_DATA(~Hzk[2*no][t]); //显示汉字的上半部分
				else
					OLED_WR_DATA(Hzk[2*no][t]); //显示汉字的上半部分
        }

		OLED_Set_Pos(x,y+1);
    for(t=0;t<16;t++)
		{
				if (Color_Turn)
					OLED_WR_DATA(~Hzk[2*no+1][t]); //显示汉字的上半部分
				else
					OLED_WR_DATA(Hzk[2*no+1][t]);//显示汉字的上半部分

         }
}

/**
 * @function: void OLED_DrawBMP(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t *  BMP,uint8_t Color_Turn)
 * @description: 在OLED特定区域显示BMP图片
 * @param {uint8_t} x0图像开始显示横坐标  x0:0~127
 * @param {uint8_t} y0图像开始显示纵坐标  y0:0~7
 * @param {uint8_t} x1图像结束显示横坐标  x1:1~128
 * @param {uint8_t} y1图像结束显示纵坐标  y1:1~8
 * @param {uint8_t} *BMP待显示的图像数据
 * @param {uint8_t} Color_Turn是否反相显示(1反相、0不反相)
 * @return {*}
 */
void OLED_DrawBMP(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t *  BMP,uint8_t Color_Turn)
{
   uint32_t j = 0;
   uint8_t x = 0, y = 0;

  if(y1%8==0)
		y = y1/8;
  else
		y = y1/8 + 1;
	for(y=y0;y<y1;y++)
	{
		OLED_Set_Pos(x0,y);
    for(x=x0;x<x1;x++)
		{
			if (Color_Turn)
				OLED_WR_DATA(~BMP[j++]);//显示反相图片
			else
				OLED_WR_DATA(BMP[j++]);//显示图片

		}
	}
}


/**
 * @function: void OLED_HorizontalShift(uint8_t direction)
 * @description: 屏幕内容水平全屏滚动播放
 * @param {uint8_t} direction			LEFT	   0x27     	RIGHT  0x26
 * @return {*}
 */
void OLED_HorizontalShift(uint8_t direction)

{
	OLED_WR_CMD(0x2e);//停止滚动
	OLED_WR_CMD(direction);//设置滚动方向
	OLED_WR_CMD(0x00);//虚拟字节设置,默认为0x00
	OLED_WR_CMD(0x00);//设置开始页地址
	OLED_WR_CMD(0x07);//设置每个滚动步骤之间的时间间隔的帧频
    //  0x00-5帧, 0x01-64帧, 0x02-128帧, 0x03-256帧, 0x04-3帧, 0x05-4帧, 0x06-25帧, 0x07-2帧,
	OLED_WR_CMD(0x07);//设置结束页地址
	OLED_WR_CMD(0x00);//虚拟字节设置,默认为0x00
	OLED_WR_CMD(0xff);//虚拟字节设置,默认为0xff
	OLED_WR_CMD(0x2f);//开启滚动-0x2f,禁用滚动-0x2e,禁用需要重写数据
}

/**
 * @function: void OLED_Some_HorizontalShift(uint8_t direction,uint8_t start,uint8_t end)
 * @description: 屏幕部分内容水平滚动播放
 * @param {uint8_t} direction			LEFT	   0x27     	RIGHT  0x26
 * @param {uint8_t} start 开始页地址  0x00~0x07
 * @param {uint8_t} end  结束页地址  0x01~0x07
 * @return {*}
 */
void OLED_Some_HorizontalShift(uint8_t direction,uint8_t start,uint8_t end)
{
	OLED_WR_CMD(0x2e);//停止滚动
	OLED_WR_CMD(direction);//设置滚动方向
	OLED_WR_CMD(0x00);//虚拟字节设置,默认为0x00
	OLED_WR_CMD(start);//设置开始页地址
	OLED_WR_CMD(0x07);//设置每个滚动步骤之间的时间间隔的帧频,0x07即滚动速度2帧
	OLED_WR_CMD(end);//设置结束页地址
	OLED_WR_CMD(0x00);//虚拟字节设置,默认为0x00
	OLED_WR_CMD(0xff);//虚拟字节设置,默认为0xff
	OLED_WR_CMD(0x2f);//开启滚动-0x2f,禁用滚动-0x2e,禁用需要重写数据

}

/**
 * @function: void OLED_VerticalAndHorizontalShift(uint8_t direction)
 * @description: 屏幕内容垂直水平全屏滚动播放
 * @param {uint8_t} direction				右上滚动	 0x29
 *                                                            左上滚动   0x2A
 * @return {*}
 */
void OLED_VerticalAndHorizontalShift(uint8_t direction)
{
	OLED_WR_CMD(0x2e);//停止滚动
	OLED_WR_CMD(direction);//设置滚动方向
	OLED_WR_CMD(0x01);//虚拟字节设置
	OLED_WR_CMD(0x00);//设置开始页地址
	OLED_WR_CMD(0x07);//设置每个滚动步骤之间的时间间隔的帧频,即滚动速度
	OLED_WR_CMD(0x07);//设置结束页地址
	OLED_WR_CMD(0x01);//垂直滚动偏移量
	OLED_WR_CMD(0x00);//虚拟字节设置,默认为0x00
	OLED_WR_CMD(0xff);//虚拟字节设置,默认为0xff
	OLED_WR_CMD(0x2f);//开启滚动-0x2f,禁用滚动-0x2e,禁用需要重写数据
}

/**
 * @function: void OLED_DisplayMode(uint8_t mode)
 * @description: 屏幕内容取反显示
 * @param {uint8_t} direction			ON	0xA7  ,
 *                                                          OFF	0xA6	默认此模式,设置像素点亮
 * @return {*}
 */
void OLED_DisplayMode(uint8_t mode)
{
	OLED_WR_CMD(mode);
}

/**
 * @function: void OLED_IntensityControl(uint8_t intensity)
 * @description: 屏幕亮度调节
 * @param  {uint8_t} intensity	0x00~0xFF,RESET=0x7F
 * @return {*}
 */
void OLED_IntensityControl(uint8_t intensity)
{
	OLED_WR_CMD(0x81);
	OLED_WR_CMD(intensity);
}


 oled_font.c 

//8*6 ASCII字符集点阵
const unsigned char F6x8[][6] =
{
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// sp
	0x00, 0x00, 0x00, 0x2f, 0x00, 0x00,// !
	0x00, 0x00, 0x07, 0x00, 0x07, 0x00,// "
	0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14,// #
	0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12,// $
	0x00, 0x62, 0x64, 0x08, 0x13, 0x23,// %
	0x00, 0x36, 0x49, 0x55, 0x22, 0x50,// &
	0x00, 0x00, 0x05, 0x03, 0x00, 0x00,// '
	0x00, 0x00, 0x1c, 0x22, 0x41, 0x00,// (
	0x00, 0x00, 0x41, 0x22, 0x1c, 0x00,// )
	0x00, 0x14, 0x08, 0x3E, 0x08, 0x14,// *
	0x00, 0x08, 0x08, 0x3E, 0x08, 0x08,// +
	0x00, 0x00, 0x00, 0xA0, 0x60, 0x00,// ,
	0x00, 0x08, 0x08, 0x08, 0x08, 0x08,// -
	0x00, 0x00, 0x60, 0x60, 0x00, 0x00,// .
	0x00, 0x20, 0x10, 0x08, 0x04, 0x02,// /
	0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E,// 0
	0x00, 0x00, 0x42, 0x7F, 0x40, 0x00,// 1
	0x00, 0x42, 0x61, 0x51, 0x49, 0x46,// 2
	0x00, 0x21, 0x41, 0x45, 0x4B, 0x31,// 3
	0x00, 0x18, 0x14, 0x12, 0x7F, 0x10,// 4
	0x00, 0x27, 0x45, 0x45, 0x45, 0x39,// 5
	0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30,// 6
	0x00, 0x01, 0x71, 0x09, 0x05, 0x03,// 7
	0x00, 0x36, 0x49, 0x49, 0x49, 0x36,// 8
	0x00, 0x06, 0x49, 0x49, 0x29, 0x1E,// 9
	0x00, 0x00, 0x36, 0x36, 0x00, 0x00,// :
	0x00, 0x00, 0x56, 0x36, 0x00, 0x00,// ;
	0x00, 0x08, 0x14, 0x22, 0x41, 0x00,// <
	0x00, 0x14, 0x14, 0x14, 0x14, 0x14,// =
	0x00, 0x00, 0x41, 0x22, 0x14, 0x08,// >
	0x00, 0x02, 0x01, 0x51, 0x09, 0x06,// ?
	0x00, 0x32, 0x49, 0x59, 0x51, 0x3E,// @
	0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C,// A
	0x00, 0x7F, 0x49, 0x49, 0x49, 0x36,// B
	0x00, 0x3E, 0x41, 0x41, 0x41, 0x22,// C
	0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C,// D
	0x00, 0x7F, 0x49, 0x49, 0x49, 0x41,// E
	0x00, 0x7F, 0x09, 0x09, 0x09, 0x01,// F
	0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A,// G
	0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F,// H
	0x00, 0x00, 0x41, 0x7F, 0x41, 0x00,// I
	0x00, 0x20, 0x40, 0x41, 0x3F, 0x01,// J
	0x00, 0x7F, 0x08, 0x14, 0x22, 0x41,// K
	0x00, 0x7F, 0x40, 0x40, 0x40, 0x40,// L
	0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F,// M
	0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F,// N
	0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E,// O
	0x00, 0x7F, 0x09, 0x09, 0x09, 0x06,// P
	0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E,// Q
	0x00, 0x7F, 0x09, 0x19, 0x29, 0x46,// R
	0x00, 0x46, 0x49, 0x49, 0x49, 0x31,// S
	0x00, 0x01, 0x01, 0x7F, 0x01, 0x01,// T
	0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F,// U
	0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F,// V
	0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F,// W
	0x00, 0x63, 0x14, 0x08, 0x14, 0x63,// X
	0x00, 0x07, 0x08, 0x70, 0x08, 0x07,// Y
	0x00, 0x61, 0x51, 0x49, 0x45, 0x43,// Z
	0x00, 0x00, 0x7F, 0x41, 0x41, 0x00,// [
	0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55,// 55
	0x00, 0x00, 0x41, 0x41, 0x7F, 0x00,// ]
	0x00, 0x04, 0x02, 0x01, 0x02, 0x04,// ^
	0x00, 0x40, 0x40, 0x40, 0x40, 0x40,// _
	0x00, 0x00, 0x01, 0x02, 0x04, 0x00,// '
	0x00, 0x20, 0x54, 0x54, 0x54, 0x78,// a
	0x00, 0x7F, 0x48, 0x44, 0x44, 0x38,// b
	0x00, 0x38, 0x44, 0x44, 0x44, 0x20,// c
	0x00, 0x38, 0x44, 0x44, 0x48, 0x7F,// d
	0x00, 0x38, 0x54, 0x54, 0x54, 0x18,// e
	0x00, 0x08, 0x7E, 0x09, 0x01, 0x02,// f
	0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C,// g
	0x00, 0x7F, 0x08, 0x04, 0x04, 0x78,// h
	0x00, 0x00, 0x44, 0x7D, 0x40, 0x00,// i
	0x00, 0x40, 0x80, 0x84, 0x7D, 0x00,// j
	0x00, 0x7F, 0x10, 0x28, 0x44, 0x00,// k
	0x00, 0x00, 0x41, 0x7F, 0x40, 0x00,// l
	0x00, 0x7C, 0x04, 0x18, 0x04, 0x78,// m
	0x00, 0x7C, 0x08, 0x04, 0x04, 0x78,// n
	0x00, 0x38, 0x44, 0x44, 0x44, 0x38,// o
	0x00, 0xFC, 0x24, 0x24, 0x24, 0x18,// p
	0x00, 0x18, 0x24, 0x24, 0x18, 0xFC,// q
	0x00, 0x7C, 0x08, 0x04, 0x04, 0x08,// r
	0x00, 0x48, 0x54, 0x54, 0x54, 0x20,// s
	0x00, 0x04, 0x3F, 0x44, 0x40, 0x20,// t
	0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C,// u
	0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C,// v
	0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C,// w
	0x00, 0x44, 0x28, 0x10, 0x28, 0x44,// x
	0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C,// y
	0x00, 0x44, 0x64, 0x54, 0x4C, 0x44,// z
	0x14, 0x14, 0x14, 0x14, 0x14, 0x14,// horiz lines
};

//16*8 ASCII集点阵
const unsigned char F8X16[]=
{
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//sp /0
 0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//!  /1
 0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//"  /2
 0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//#  /3
 0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$  /4
 0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,//%  /5
 0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,//&  /6
 0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//'  /7
 0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00,//(  /8
 0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00,//)  /9
 0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00,//*  /10
 0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00,//+  /11
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00,//,  /12
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,//-  /13
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,//.  /14
 0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00,///  /15
 0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,//0  /16
 0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//1  /17
 0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,//2  /18
 0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,//3  /19
 0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,//4  /20
 0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,//5  /21
 0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,//6  /22
 0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,//7  /23
 0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,//8  /24
 0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,//9  /25
 0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,//:  /26
 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00,//;  /27
 0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00,//<  /28
 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,//=  /29
 0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00,//>  /30
 0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00,//?  /31
 0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00,//@  /32
 0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,//A  /33
 0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,//B  /34
 0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,//C  /35
 0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,//D  /36
 0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,//E  /37
 0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,//F  /38
 0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,//G  /39
 0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,//H  /40
 0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//I  /41
 0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,//J  /42
 0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,//K  /43
 0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,//L  /44
 0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,//M  /45
 0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,//N  /46
 0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,//O  /47
 0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,//P  /48
 0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,//Q  /49
 0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,//R  /50
 0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,//S  /51
 0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//T  /52
 0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//U  /53
 0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,//V  /54
 0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,//W  /55
 0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,//X  /56
 0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//Y  /57
 0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,//Z  /58
 0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00,//[  /59
 0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00,//\  /60
 0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00,//]  /61
 0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//^  /62
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,//_  /63
 0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//`  /64
 0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,//a  /65
 0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00,//b  /66
 0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00,//c  /67
 0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20,//d  /68
 0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00,//e  /69
 0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//f  /70
 0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00,//g  /71
 0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//h  /72
 0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//i  /73
 0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,//j  /74
 0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00,//k  /75
 0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//l  /76
 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F,//m  /77
 0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//n  /78
 0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//o  /79
 0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00,//p  /80
 0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80,//q  /81
 0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00,//r  /82
 0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00,//s  /83
 0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00,//t  /84
 0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20,//u  /85
 0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00,//v  /86
 0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00,//w  /87
 0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00,//x  /88
 0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00,//y  /89
 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00,//z  /90
 0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40,//{  /91
 0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00,//}  /92
 0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00};//|  /93

//部分汉字 阴码,列行式,逆向 
const unsigned char Hzk[][32]=
{
		{0x40,0x22,0x14,0x08,0xF4,0x02,0x00,0xF8,0x08,0x08,0xFF,0x08,0x08,0xF8,0x00,0x00},
		{0x08,0x44,0x82,0x41,0x3F,0x00,0x20,0x63,0x21,0x21,0x3F,0x21,0x29,0x33,0x60,0x00},/*"?",0*/
		{0x20,0x10,0xE8,0x24,0x27,0x24,0x24,0xE4,0x24,0x34,0x2C,0x20,0xE0,0x00,0x00,0x00},
		{0x80,0x60,0x1F,0x09,0x09,0x09,0x09,0x7F,0x09,0x09,0x49,0x89,0x7F,0x00,0x00,0x00},/*"?",1*/
		{0x00,0x00,0x7C,0x54,0x55,0x56,0x54,0x7C,0x54,0x56,0x55,0x54,0x7C,0x00,0x00,0x00},
		{0x01,0x01,0x01,0xFD,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0xFD,0x01,0x01,0x01,0x00},/*"?",2*/



};
//128*64图片
unsigned char BMP1[]={
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x00,0x40,0xE0,0xE0,0x60,
		0x00,0x40,0x80,0x00,0x00,0x00,0x80,0x80,0x60,0x60,0x10,0x00,0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x00,0x90,0xB2,0x66,0x6E,0xEC,0xC9,0x83,0x02,0x00,0x40,0x80,
		0x00,0x0C,0x0C,0x01,0x03,0x07,0x0F,0x11,0x20,0x40,0x80,0x00,0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x08,0x18,0x3B,0x37,0x66,0xEE,0xEE,0xCC,0x88,0x80,0x00,0x00,
		0x01,0x02,0x04,0x08,0x10,0x10,0x10,0x10,0x10,0x10,0x0F,0x00,0x00,0x00,0x00,0x00,
		0x00,0x3E,0x40,0x40,0x40,0x3E,0x00,0x7E,0x04,0x08,0x10,0x7E,0x00,0x7E,0x00,0x3C,
		0x42,0x42,0x42,0x42,0x00,0x7E,0x42,0x42,0x42,0x7E,0x00,0x7E,0x04,0x08,0x10,0x7E,/*"C:\Users\HP\Desktop\UNICORN.BMP",0*/
};

 注意:OLED模块要配合I2C使用!!!

6.I2C模块

I2C1配置

注意:不用开启全局中断!!!

7.ADC采集模块

ADC1配置

 

注意:要开启ADC1的DMA全局中断 !!!

8.USART模块

USART1配置

注意:要开启USART1的全局中断 !!!  

USART3配置

 

 注意:要开启USART3的DMA全局中断 !!!

9.调试模块

printf函数重定义

/*********************************************
              printf()重定义
*********************************************/

/* 禁用半主机模式 */
#pragma import(__use_no_semihosting)   

/* 标准库需要的支持函数 */                 
struct __FILE 
{ 
	int handle; 
}; 

FILE __stdout;

/* 定义_sys_exit()以避免使用半主机模式 */ 
void _sys_exit(int x) {
    x = x;
}

/* 重定义fput函数,将printf输出定向到USART1 */
int fputc(int ch, FILE *f) {
    uint8_t c = (uint8_t)ch;
    HAL_UART_Transmit(&huart1, &c, 1, 100);  // 通过USART1发送字符
    return ch;
}

注意:调试模块配合USART1使用!!!    这段代码一般放在usart.c里 

10.操作系统模块

freertos配置

说明:1.创建了五个任务,优先级从大到小依次为ADC、DHT11、OLED、WiFi、default

           2.创建了互斥量

注意:使用CMSIS-V1!!!

11.FreeRTOS模块

freertos.c

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "adc.h"
#include "dht11.h"
#include "oled.h"
#include "stdio.h"
#include "string.h"
#include "detection.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

SensorData g_sensorData;  //传感器数据
	
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

/* USER CODE END Variables */
osThreadId defaultTaskHandle;
osThreadId OLEDHandle;
osThreadId ADCHandle;
osThreadId DHT11Handle;
osThreadId WiFiHandle;
osMutexId SensorMutexHandle;

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartDefaultTask(void const * argument);
void OLED_Task(void const * argument);
void ADC_Task(void const * argument);
void DHT11_Task(void const * argument);
void WiFi_Task(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */
  /* USER CODE END Init */
  /* Create the mutex(es) */
  /* definition and creation of SensorMutex */
  osMutexDef(SensorMutex);
  SensorMutexHandle = osMutexCreate(osMutex(SensorMutex));

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of defaultTask */
  osThreadDef(defaultTask, StartDefaultTask, osPriorityLow, 0, 128);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

  /* definition and creation of OLED */
  osThreadDef(OLED, OLED_Task, osPriorityNormal, 0, 128);
  OLEDHandle = osThreadCreate(osThread(OLED), NULL);

  /* definition and creation of ADC */
  osThreadDef(ADC, ADC_Task, osPriorityHigh, 0, 128);
  ADCHandle = osThreadCreate(osThread(ADC), NULL);

  /* definition and creation of DHT11 */
  osThreadDef(DHT11, DHT11_Task, osPriorityAboveNormal, 0, 128);
  DHT11Handle = osThreadCreate(osThread(DHT11), NULL);

  /* definition and creation of WiFi */
  osThreadDef(WiFi, WiFi_Task, osPriorityBelowNormal, 0, 128);
  WiFiHandle = osThreadCreate(osThread(WiFi), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartDefaultTask */
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
  for(;;)
  {
		HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 翻转PC13(板载LED)电平
		osDelay(400);
	}
  /* USER CODE END StartDefaultTask */
}

/* USER CODE BEGIN Header_OLED_Task */
/**
* @brief Function implementing the OLED thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_OLED_Task */
void OLED_Task(void const * argument)
{
  /* USER CODE BEGIN OLED_Task */
	SensorData oleddata;

	OLED_ShowString(4, 2, "humidity:", 12, 0);
	OLED_ShowString(4, 3, "temperature:", 12, 0);
	OLED_ShowString(4, 5, "thermistor:", 12, 0);
	OLED_ShowString(4, 6, "photoresistor:", 12, 0);
  /* Infinite loop */
  for(;;)
  {
		osMutexWait(SensorMutexHandle, osWaitForever);
		oleddata = g_sensorData;  // 拷贝全局数据到本地
		osMutexRelease(SensorMutexHandle);

		OLED_ShowNum(112, 2, oleddata.humidity, 2, 12, 0);        // 显示湿度(湿度范围0-99)
		OLED_ShowNum(112, 3, oleddata.temperature, 2, 12, 0);     // 显示温度(温度范围0-99)
		OLED_ShowNum(100, 5, oleddata.thermistor, 4, 12, 0);      // 显示热敏电阻的ADC值(0-4095)
		OLED_ShowNum(100, 6, oleddata.photoresistor, 4, 12, 0);   // 显示光敏电阻的ADC值(0-4095)

		osDelay(500);
  }
  /* USER CODE END OLED_Task */
}

/* USER CODE BEGIN Header_ADC_Task */
/**
* @brief Function implementing the ADC thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_ADC_Task */
void ADC_Task(void const * argument)
{
  /* USER CODE BEGIN ADC_Task */
  /* Infinite loop */
  for(;;)
  {
		osMutexWait(SensorMutexHandle, osWaitForever);
		g_sensorData.thermistor = adcDmaBuffer[0];
		g_sensorData.photoresistor = adcDmaBuffer[1];
		osMutexRelease(SensorMutexHandle);
		
		//printf("adc_val:%4d\t illuminance:%4d\r\n", g_sensorData.adc_val, g_sensorData.illuminance);
		osDelay(100);
	}
  /* USER CODE END ADC_Task */
}

/* USER CODE BEGIN Header_DHT11_Task */
/**
* @brief Function implementing the DHT11 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_DHT11_Task */
void DHT11_Task(void const * argument)
{
  /* USER CODE BEGIN DHT11_Task */
  /* Infinite loop */
  for(;;)
  {
		uint8_t humidity, temperature;
		DHT11_Read_Data(&humidity, &temperature); // 读取DHT11
		
		osMutexWait(SensorMutexHandle, osWaitForever);
		g_sensorData.humidity = humidity;
		g_sensorData.temperature = temperature;
		osMutexRelease(SensorMutexHandle);
		
		//printf("humidity:%2d\t illuminance:%2d\r\n", g_sensorData.humidity, g_sensorData.temperature);
    osDelay(2000);  // DHT11 采集至少2s
	}
  /* USER CODE END DHT11_Task */
}

/* USER CODE BEGIN Header_WiFi_Task */
/**
* @brief Function implementing the WiFi thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_WiFi_Task */
void WiFi_Task(void const * argument)
{
  /* USER CODE BEGIN WiFi_Task */
	SensorData wifidata;
  /* Infinite loop */
  for(;;)
  {
		osMutexWait(SensorMutexHandle, osWaitForever);
		wifidata = g_sensorData;  // 拷贝全局数据到本地
		osMutexRelease(SensorMutexHandle);
		
		wifidata.head = 0xAA;
		wifidata.tail = 0x55;
		memcpy(esp32TxBuffer, &wifidata, ESP32_TX_BUF_SIZE);
		
		//测试
//    printf("adc_val:%4d   illuminance:%4d   temperature:%2d   humidity:%2d\r\n",
//           wifidata.thermistor, wifidata.photoresistor, wifidata.temperature, wifidata.humidity);	
		for(int i =0; i < ESP32_TX_BUF_SIZE; i++)
		{
			printf("%02X", esp32TxBuffer[i]);
		}
		printf("\r\n");
		
		osDelay(10);
  }
  /* USER CODE END WiFi_Task */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

说明:通过互斥量,保证多个线程对全局变量的互斥访问,避免数据竞争,保证数据一致性。

12.DMA开启和硬件检测模块

detection.c

// 创建 ADC+DMA 缓冲区 
uint16_t adcDmaBuffer[ADC_DMA_BUFFER_SIZE];
// 创建串口发送缓冲区
uint8_t esp32TxBuffer[ESP32_TX_BUF_SIZE];


void detection(void)
{
  // 校验 ADC
	while(HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK) 
	{
		printf("ADC Calibration Failed!\r\n");
		HAL_Delay(100); 
	}
	printf("ADC Calibration Success!\r\n");
	
	// 开启 ADC+DMA 转运
	while(HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adcDmaBuffer, ADC_DMA_BUFFER_SIZE) != HAL_OK)
  {
		printf("ADC DMA Start Error!\r\n");
		HAL_Delay(100); 
	}
	printf("ADC DMA Start Success!\r\n");
	
	// 开启 USART3+DMA 传输
	while(HAL_UART_Transmit_DMA(&huart3, (uint8_t *)esp32TxBuffer, ESP32_TX_BUF_SIZE)!= HAL_OK)
  {
		printf("USART3 DMA Transmit Error!\r\n");
		HAL_Delay(100); 
	}
	printf("USART3 DMA Transmit Success!\r\n");
	
	// 检测 DHT11 连接
	while(DHT11_Check() == 0) 
  {
	  printf("DHT11 Connection Failed!\r\n");
		HAL_Delay(100);
  }
  printf("DHT11 Connection Success !\r\n");
}

说明:因为USART3和ADC1都开启了DMA循环模式,所以只需要启动一次

detection.h

#ifndef DETECTION_H_
#define DETECTION_H_

#include "main.h"
#include "stdio.h"
#include "dht11.h"

extern ADC_HandleTypeDef hadc1;
extern UART_HandleTypeDef huart3;

#pragma pack(1)
typedef struct {
    uint8_t head;
    uint16_t thermistor;
    uint16_t photoresistor;
    uint8_t temperature;
    uint8_t humidity;
    uint8_t tail;
} SensorData;
#pragma pack()

#define ADC_DMA_BUFFER_SIZE 32 
extern uint16_t adcDmaBuffer[ADC_DMA_BUFFER_SIZE];

#define ESP32_TX_BUF_SIZE sizeof(SensorData)  
extern uint8_t esp32TxBuffer[ESP32_TX_BUF_SIZE];

void detection(void);

#endif 

13.注意事项

1.通过串口发送的是结构体的二进制数据,增加了帧头帧尾,方便接收端的数据解析

2.一定要控制串口发送缓冲区的大小取决于结构体的大小

(二)ESP32程序设计

1.程序代码

#include <Arduino.h>
#include "WiFi.h"
#include "PubSubClient.h"
#include "ArduinoJson.h"
#include <HardwareSerial.h>

// 与STM32完全一致的结构体定义
#pragma pack(1)
typedef struct {
    uint8_t head;
    uint16_t thermistor;     // uint16_t类型(2字节)
    uint16_t photoresistor;  // uint16_t类型(2字节)
    uint8_t temperature;     // uint8_t类型(1字节)
    uint8_t humidity;        // uint8_t类型(1字节)
    uint8_t tail;
} SensorData;
#pragma pack()

// 全局变量:存储解析成功的传感器数据
SensorData parsedSensorData;
bool hasValidData = false;  // 标记是否有有效数据(避免上传空值)

#define SENSOR_DATA_LEN sizeof(SensorData)
#define RX_BUFFER_SIZE 128  // 接收缓冲区大小

uint8_t rx_buffer[RX_BUFFER_SIZE];  // 接收缓冲区
size_t rx_len = 0;                  // 缓冲区数据长度

// WiFi和MQTT配置
const char* ssid = "vivo X23";        //wifi名        
const char* password = "123456789";     //wifi密码     
const char* mqtt_server = "mqtts.heclouds.com";    //onenet 的 IP地址 mqtts.heclouds.com 183.230.40.96
const int port = 1883;    //端口号

#define mqtt_pubid "899v38taqa"     //产品ID
#define mqtt_devid "ESP32"    //设备名称
#define mqtt_password "version=2018-10-31&res=products%2F899v38taqa%2Fdevices%2FESP32&et=1911074531&method=md5&sign=5pHVGmmr3GiSN%2BGbBVekWQ%3D%3D"    //鉴权信息

WiFiClient espClient;
PubSubClient client(espClient);

// MQTT主题
#define ONENET_TOPIC_PROP_POST  "$sys/" mqtt_pubid "/" mqtt_devid "/thing/property/post"
#define ONENET_TOPIC_PROP_SET  "$sys/" mqtt_pubid "/" mqtt_devid "/thing/property/set"
#define ONENET_TOPIC_PROP_SET_REPLY "$sys/" mqtt_pubid "/" mqtt_devid "/thing/property/set_reply"
#define ONENET_TOPIC_PROP_GET "$sys/" mqtt_pubid "/" mqtt_devid "/thing/property/get"
#define ONENET_TOPIC_PROP_GET_REPLY  "$sys/" mqtt_pubid "/" mqtt_devid "/thing/property/get_reply"

// JSON格式宏
// %hu:匹配uint16_t(adc_val/illuminance);%hhu:匹配uint8_t(temperature/humidity)
#define PARAMS_FORMAT "{\"thermistor\": {\"value\": %hu}, \"photoresistor\": {\"value\": %hu}, \"temperature\": {\"value\": %hhu}, \"humidity\": {\"value\": %hhu}}"
#define FULL_FORMAT "{\"id\": \"123\", \"version\": \"1.0\", \"params\": %s}"

// 连接WiFi
void setupWifi() {
  delay(10);
  Serial.println("Connecting to WiFi...");
  WiFi.begin(ssid, password);
  while (!WiFi.isConnected()) {
    Serial.print(".");
    delay(500);
  }
  Serial.println("\nWiFi connected!");
  Serial.println("IP address: " + WiFi.localIP().toString());
}

// MQTT重连
void clientReconnect() {
  while (!client.connected()) {
    Serial.println("Reconnecting to MQTT...");
    if (client.connect(mqtt_devid, mqtt_pubid, mqtt_password)) {
      Serial.println("MQTT connected");
      client.subscribe(ONENET_TOPIC_PROP_SET);
      client.subscribe(ONENET_TOPIC_PROP_GET);
    } else {
      Serial.print("Failed, state: ");
      Serial.println(client.state());
      delay(5000);
    }
  }
}

// // MQTT回调(可拓展)
// void callback(char *topic, byte *payload, unsigned int length) {
//   Serial.println("Received message:");
//   Serial.println(topic);
//   for (size_t i = 0; i < length; i++) {
//     Serial.print((char)payload[i]);
//   }
//   Serial.println();

//   if (strstr(topic, ONENET_TOPIC_PROP_SET)) {
//     DynamicJsonDocument doc(100);
//     DeserializationError error = deserializeJson(doc, payload);
//     if (error) {
//       Serial.println("Parse JSON failed");
//       return;
//     }
//     String id = doc["id"].as<String>();
//     char reply[100];
//     sprintf(reply, "{\"id\": \"%s\",\"code\":200,\"msg\":\"success\"}", id.c_str());
//     client.publish(ONENET_TOPIC_PROP_SET_REPLY, reply);
//   }

//   if (strstr(topic, ONENET_TOPIC_PROP_GET)) {
//     DynamicJsonDocument doc(100);
//     DeserializationError error = deserializeJson(doc, payload);
//     if (error) {
//       Serial.println("Parse JSON failed");
//       return;
//     }
//     String id = doc["id"].as<String>();
//     char reply[200];
//     // 若有有效数据,回复当前传感器值
//     if (hasValidData) {
//       sprintf(reply, "{\"id\": \"%s\",\"code\":200,\"data\":{\"adc_val\":%hu,\"illuminance\":%hu,\"temperature\":%hhu,\"humidity\":%hhu}}",
//               id.c_str(), parsedSensorData.thermistor, parsedSensorData.photoresistor,
//               parsedSensorData.temperature, parsedSensorData.humidity);
//     } else {
//       sprintf(reply, "{\"id\": \"%s\",\"code\":400,\"msg\":\"no data\"}", id.c_str());
//     }
//     client.publish(ONENET_TOPIC_PROP_GET_REPLY, reply);
//   }
// }

// 解析传感器数据
void parse_sensor_data(void) {
  for (size_t i = 0; i <= rx_len - SENSOR_DATA_LEN; i++) {
    // 检测帧头(0xAA)和帧尾(0x55)
    if (rx_buffer[i] == 0xAA && rx_buffer[i + SENSOR_DATA_LEN - 1] == 0x55) {
      // 将解析到的数据复制到全局变量
      memcpy(&parsedSensorData, &rx_buffer[i], SENSOR_DATA_LEN);
      hasValidData = true;  // 标记有有效数据

      // 打印解析结果(调试)
      // Serial.print("解析成功:");
      // Serial.print("热敏传感器=");
      // Serial.print(parsedSensorData.thermistor);
      // Serial.print(",光敏传感器=");
      // Serial.print(parsedSensorData.photoresistor);
      // Serial.print(",温度=");
      // Serial.print(parsedSensorData.temperature);
      // Serial.print("℃,湿度=");
      // Serial.print(parsedSensorData.humidity);
      // Serial.println("%");

      // 移除已解析数据,保留剩余内容
      memmove(rx_buffer, rx_buffer + i + SENSOR_DATA_LEN, rx_len - (i + SENSOR_DATA_LEN));
      rx_len -= (i + SENSOR_DATA_LEN);
      return;
    }
  }
  // 缓冲区快满时重置
  if (rx_len > RX_BUFFER_SIZE * 0.8) {
    rx_len = 0;
    Serial.println("未找到完整数据包,缓冲区已重置");
  }
}

// 读取串口数据
void read_sensor_data(void) {
  while (Serial2.available() > 0 && rx_len < RX_BUFFER_SIZE) {
    rx_buffer[rx_len++] = Serial2.read();
  }
  if (rx_len >= SENSOR_DATA_LEN) {
    parse_sensor_data();
  }
}

// 上传数据到云端
void sendtocloud(void) {
  if (client.connected() && hasValidData) {  // 仅当有有效数据时上传
    char params_str[128] = {0};
    char full_json[256] = {0};

    // 用全局变量中的解析数据填充params
    sprintf(params_str, PARAMS_FORMAT,
            parsedSensorData.thermistor,       // 使用解析后的ADC值
            parsedSensorData.photoresistor,   // 使用解析后的光照度
            parsedSensorData.temperature,   // 使用解析后的温度
            parsedSensorData.humidity);     // 使用解析后的湿度

    // 组装完整JSON
    sprintf(full_json, FULL_FORMAT, params_str);

    // 发布到云端
    if (client.publish(ONENET_TOPIC_PROP_POST, full_json)) {
      Serial.print("上传成功:");
      Serial.println(full_json);
    } else {
      Serial.println("上传失败!");
    }
  } else if (!hasValidData) {
    Serial.println("无有效数据,暂不上传");
  }
}

void setup() {
  Serial.begin(115200);
  Serial2.begin(115200, SERIAL_8N1, 16, 17);  // 初始化串口2(接STM32)
  delay(3000);

  setupWifi();
  client.setServer(mqtt_server, port);
  //client.setCallback(callback); (可拓展)
  delay(2000);

  clientReconnect();  // 连接MQTT
}

void loop() {
  if (!WiFi.isConnected()) {
    setupWifi();
  }
  if (!client.connected()) {
    clientReconnect();
  }
  client.loop();

  read_sensor_data();  // 持续读取并解析STM32数据

  sendtocloud();  //上传数据到云端
}

2.代码说明

// WiFi和MQTT配置
const char* ssid = "vivo X23";        //wifi名        
const char* password = "123456789";     //wifi密码     

#define mqtt_pubid "899v38taqa"     //产品ID
#define mqtt_devid "ESP32"    //设备名称
#define mqtt_password "version=2018-10-31&res=products%2F899v38taqa%2Fdevices%2FESP32&et=1911074531&method=md5&sign=5pHVGmmr3GiSN%2BGbBVekWQ%3D%3D"    //鉴权信息

#define PARAMS_FORMAT "{\"thermistor\": {\"value\": %hu}, \"photoresistor\": {\"value\": %hu}, \"temperature\": {\"value\": %hhu}, \"humidity\": {\"value\": %hhu}}"

说明:上述内容需要修改,详细内容查看文章ESP32连接OneNet

四、测试结果

(一)STM32测试

1.OLED打印

2.串口发送数据

(二)ESP32测试

1.上传数据

2.云端显示

五、参考文章 

1.文章框架:stm32+DHT11温湿度检测系统_dht11stm32温度检测系统框架-CSDN博客

2.温湿度采集: 【STM32(HAL库)--DHT11温湿度传感器】_stm32 dh11库函数-CSDN博客

3.OLED显示: 4针0.96寸OLED的HAL库代码(硬件I2C/全代码/stm32f1/CubeMX配置/包含有正负浮点数/100%一次点亮)_hal库4针oled-CSDN博客

4.ESP32连接OneNet:【手把手教你用ESP32连接MQTT云端!!】https://www.bilibili.com/video/BV14ZuBz8E9x?vd_source=5fefab38f2696a32ccef96a4db3b4eea

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐