这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » 通过BLE灯光控制和温湿度采集【成果贴】

共3条 1/1 1 跳转至

通过BLE灯光控制和温湿度采集【成果贴】

菜鸟
2025-01-06 17:06:06     打赏

本次通过上位机蓝牙控制LED灯的亮度,并通过OLED显示。下位机收集传感器采集到的温度、湿度数据,然后通过蓝牙发送到上位机显示。(感谢视频中老师的细致讲解)

一、硬件介绍。


a150d4d9578b501eb886824b41b546b.jpg

这里使用SHT30作为温湿度传感器,使用I2C与NUCEO-WBA55CG相连接;使用了一块微雪的1.51寸透明OLED屏幕,用来展示PWM的信息。这块OLED屏幕也是使用I2C驱动,透明的OLED看上去科技感很足。

二、硬件连接。







CN4



sht30OLED

2GPIO_2



1GPIO_26




2GPIO_25
4GPIO_4



3GPIO_28PB2D15SCL4GPIO_27
65V



5GPIO_29PB1D14SDA绿绿6GPIO_30
8GND



7AVDD
AVDD


85V
10




9GND




10GPIO_32
12

IOREF

11GPIO_31PB4(LD1)D13SCK

12GPIO_35
14NRST



13GPIO_33PB3D12MISO

14GPIO_36
163V3



15GPIO_34PA15D11MOSI

16GPIO_38
185V



17GPIO_37PA12D10SS
橙 CS18GPIO_40
20GND



19GPIO_39PA9D9


20GND
22GND



21GPIO_41PA15/PA11D8


22GPIO_43
24VIN



23GPIO_42PB9/PC13D7


24GPIO_45
26GPIO_7



25GPIO_44PB0D6

白 RST26GPIO_46
28GPIO_11PA7A0

27GPIO_47PB14D5

绿 DC28GPIO_48
30GPIO_12PA6A1TIM2_CH4PWM输出29GPIO_49PB13D4


30GPIO_51
32GPIO_17PA2A2

31GPIO_50PB6(B2)D3


32GND
34GPIO_18PA1A3

33GPIO_52PB7(B3)D2


34GPIO_53
36GPIO_21PA5/PA8A4SDA
35GPIO_54PB5D1


36GPIO_56
38GPIO_22PA0/PA5A5SCL
37GPIO_55PA10D0


38GPIO_57

image.png

线接的比较乱,OLED和SHT30都是走I2C接口,其中OLED还有rst、dc、cs三条线,测试下来dc,cs两条线感觉是没有用到,可以悬空的。OLED也有SPI的接线方式,但是尝试过使用SPI去驱动OLED屏幕,在蓝牙的例程中,系统会出现卡死,暂时没能解决,所以最终选择了使用I2C方式驱动。

三、下位机编程。

参考着老师的视频,使用例程来作为基础工程,这里我使用了WBA1.5.0的例程。例程初次编译,会有一个报错,调整一下对应文件路径就可以解决了。


1736146932094.jpg

1736146968271.jpg

1736147037769.jpg

使用STM32CUBEMX,来调整硬件基础设置。这里需要使用PWM来控制LED灯的亮度,开启TIM2的CH4的PWM功能。对应的管脚PA6。调整PWM输出评论为1KHz。

image.png

配置I2C1,用来驱动SHT30和OLED。SHT30上送内容有温度和湿度两个数据,这里我使用float类型,每个数据长度就是4个字节,一共为8个字节。所以这里需要调整下位机蓝牙上送字节长度。如图所示:

image.png

接下来编程部分,添加sht3x驱动文件,添加oled驱动文件,也有尝试了使用U8g2,来驱动OLED,单独驱动没问题,放到蓝牙工程中就不能正常运行,不明白为啥。

#include "sht3x.h"  
  
#include <assert.h>  
  
/** 
 * Registers addresses. 
 */  
typedef enum  
{  
    SHT3X_COMMAND_MEASURE_HIGHREP_STRETCH = 0x2c06,  
    SHT3X_COMMAND_CLEAR_STATUS = 0x3041,  
    SHT3X_COMMAND_SOFT_RESET = 0x30A2,  
    SHT3X_COMMAND_HEATER_ENABLE = 0x306d,  
    SHT3X_COMMAND_HEATER_DISABLE = 0x3066,  
    SHT3X_COMMAND_READ_STATUS = 0xf32d,  
    SHT3X_COMMAND_FETCH_DATA = 0xe000,  
    SHT3X_COMMAND_MEASURE_HIGHREP_10HZ = 0x2737,  
    SHT3X_COMMAND_MEASURE_LOWREP_10HZ = 0x272a  
} sht3x_command_t;  
  
static uint8_t calculate_crc(const uint8_t *data, size_t length)  
{  
    uint8_t crc = 0xff;  
    for (size_t i = 0; i < length; i++) {  
        crc ^= data[i];  
        for (size_t j = 0; j < 8; j++) {  
            if ((crc & 0x80u) != 0) {  
                crc = (uint8_t)((uint8_t)(crc << 1u) ^ 0x31u);  
            } else {  
                crc <<= 1u;  
            }  
        }  
    }  
    return crc;  
}  
  
static bool sht3x_send_command(sht3x_handle_t *handle, sht3x_command_t command)  
{  
    uint8_t command_buffer[2] = {(command & 0xff00u) >> 8u, command & 0xffu};  
  
    if (HAL_I2C_Master_Transmit(handle->i2c_handle, handle->device_address << 1u, command_buffer, sizeof(command_buffer),  
                                SHT3X_I2C_TIMEOUT) != HAL_OK) {  
        return false;  
    }  
  
    return true;  
}  
  
static uint16_t uint8_to_uint16(uint8_t msb, uint8_t lsb)  
{  
    return (uint16_t)((uint16_t)msb << 8u) | lsb;  
}  
  
bool sht3x_init(sht3x_handle_t *handle)  
{  
    assert(handle->i2c_handle->Init.NoStretchMode == I2C_NOSTRETCH_DISABLE);  
    // TODO: Assert i2c frequency is not too high  
  
    uint8_t status_reg_and_checksum[3];  
    if (HAL_I2C_Mem_Read(handle->i2c_handle, handle->device_address << 1u, SHT3X_COMMAND_READ_STATUS, 2, (uint8_t*)&status_reg_and_checksum,  
                      sizeof(status_reg_and_checksum), SHT3X_I2C_TIMEOUT) != HAL_OK) {  
        return false;  
    }  
  
    uint8_t calculated_crc = calculate_crc(status_reg_and_checksum, 2);  
  
    if (calculated_crc != status_reg_and_checksum[2]) {  
        return false;  
    }  
  
    return true;  
}  
  
bool sht3x_read_temperature_and_humidity(sht3x_handle_t *handle, float *temperature, float *humidity)  
{  
    sht3x_send_command(handle, SHT3X_COMMAND_MEASURE_HIGHREP_STRETCH);  
  
    HAL_Delay(1);  
  
    uint8_t buffer[6];  
    if (HAL_I2C_Master_Receive(handle->i2c_handle, handle->device_address << 1u, buffer, sizeof(buffer), SHT3X_I2C_TIMEOUT) != HAL_OK) {  
        return false;  
    }  
  
    uint8_t temperature_crc = calculate_crc(buffer, 2);  
    uint8_t humidity_crc = calculate_crc(buffer + 3, 2);  
    if (temperature_crc != buffer[2] || humidity_crc != buffer[5]) {  
        return false;  
    }  
    uint16_t temperature_raw = uint8_to_uint16(buffer[0], buffer[1]);  
    uint16_t humidity_raw = uint8_to_uint16(buffer[3], buffer[4]);  
  
    *temperature = -45.0f + 175.0f * (float)temperature_raw / 65535.0f;  
    *humidity = 100.0f * (float)humidity_raw / 65535.0f;  
  
    return true;  
}  
  
bool sht3x_set_header_enable(sht3x_handle_t *handle, bool enable)  
{  
    if (enable) {  
        return sht3x_send_command(handle, SHT3X_COMMAND_HEATER_ENABLE);  
    } else {  
        return sht3x_send_command(handle, SHT3X_COMMAND_HEATER_DISABLE);  
    }  
}
/**
 * 基于硬件IIC通信的OLED驱动程序
 * API完全打包好的直接选择你的硬件IIC口即可
 *
 */
#include "main.h"
#include "stdio.h"
#include "oled_font.h"
#include "oled.h"

uint8_t OLED_Buffer[8][128]={0};
extern I2C_HandleTypeDef hi2c1; 

/**
  * @brief  OLED写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void OLED_WriteCommand(uint8_t Command)
{
	 uint8_t *pData;
	 pData = &Command;
	 HAL_I2C_Mem_Write(&Hardware_IIC_No,0x78,0x00,I2C_MEMADD_SIZE_8BIT,pData,1,100);
}

/**
  * @brief  OLED写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void OLED_WriteData(uint8_t Data)
{
	 uint8_t *pData;
	 pData = &Data;
	HAL_I2C_Mem_Write(&Hardware_IIC_No,0x78,0x40,I2C_MEMADD_SIZE_8BIT,pData,1,100);

}

/**
  * @brief  OLED设置光标位置
  * @param  Y 以左上角为原点,向下方向的坐标,范围:0~7
  * @param  X 以左上角为原点,向右方向的坐标,范围:0~127
  * @retval 无
  */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
	OLED_WriteCommand(0xB0 | Y);					//设置Y位置
	OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));	//设置X位置低4位
	OLED_WriteCommand(0x00 | (X & 0x0F));			//设置X位置高4位
}

/**
  * @brief  OLED清屏
  * @param  无
  * @retval 无
  */
void OLED_Clear(void)
{
	uint8_t i, j;
	for (j = 0; j < 8; j++)
	{
		OLED_SetCursor(j, 0);
		for(i = 0; i < 128; i++)
		{
			OLED_WriteData(0x00);
		}
	}
}

/**
  * @brief  OLED显示一个字符
  * @param  Line 行位置,范围:1~4
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的一个字符,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
	uint8_t i;
	OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);		//设置光标位置在上半部分
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i]);			//显示上半部分内容
	}
	OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//设置光标位置在下半部分
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);		//显示下半部分内容
	}
}

/**
  * @brief  OLED显示字符串
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i++)
	{
		OLED_ShowChar(Line, Column + i, String[i]);
	}
}

/**
  * @brief  OLED次方函数
  * @retval 返回值等于X的Y次方
  */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y--)
	{
		Result *= X;
	}
	return Result;
}

/**
  * @brief  OLED显示数字(十进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~4294967295
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED显示数字(十进制,带符号数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-2147483648~2147483647
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
	uint8_t i;
	uint32_t Number1;
	if (Number >= 0)
	{
		OLED_ShowChar(Line, Column, '+');
		Number1 = Number;
	}
	else
	{
		OLED_ShowChar(Line, Column, '-');
		Number1 = -Number;
	}
	for (i = 0; i < Length; i++)
	{
		OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED显示数字(十六进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFFFFFF
  * @param  Length 要显示数字的长度,范围:1~8
  * @retval 无
  */
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i, SingleNumber;
	for (i = 0; i < Length; i++)
	{
		SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
		if (SingleNumber < 10)
		{
			OLED_ShowChar(Line, Column + i, SingleNumber + '0');
		}
		else
		{
			OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
		}
	}
}

/**
  * @brief  OLED显示数字(二进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
	}
}

/**
 *  @brief OLED显示模拟量
 *  @param Line起始位置,范围0~3
 *  @param Colum 起始列位置,0~7
 *  @param CHN 汉字序列
 */
void OLED_ShowChinese(uint8_t Line, uint8_t Column, uint8_t CHN)
{
		uint8_t i;
		OLED_SetCursor(Line*2, Column*16);		//设置光标位置在上半部分
		for (i = 0; i < 16; i++)
		{
			OLED_WriteData(GBK[CHN][i]);			//显示上半部分内容
		}
		OLED_SetCursor((Line*2)+1,Column*16);	//设置光标位置在下半部分
		for (i = 0; i < 16; i++)
		{
			OLED_WriteData(GBK[CHN][i + 16]);		//显示下半部分内容
		}
}

/**
 *  @brief OLED显示模拟量
 *  @param Pic第几张图片,范围0~3
 *  @param Colum 起始列位置,0~7
 *
 */
void OLED_ShowPicture(uint8_t Pic)
{
	uint8_t i,j;
	Pic=Pic*8;
	for(j=0;j<8;j++)
	{
		OLED_SetCursor(j,0);//开始显示
		for (i = 0; i < 128; i++)
				{
					OLED_WriteData(Picture[j+Pic][i]);			//发送数据
				}
	}
}
/*
 * 快速显示照片
 */
void OLED_FastShowPicture(uint8_t Pic)
{
	uint8_t i,j;
	Pic=Pic*8;
	for(j=0;j<8;j++)
	{
		for (i = 0; i < 128; i++)
				{
				OLED_Buffer[j][i]=(Picture[j+Pic][i]);			//发送数据
				}
	}
}
/*
 * @brief 向缓冲区写入字符数据
 * Line 1~4;
 * Column 1~16;
 */
void OLED_FastShowChar(uint8_t Line, uint8_t Column, char Char)
{
	uint8_t i;

		for (i = 0; i < 8; i++)
		{
			OLED_Buffer[(Line- 1) * 2][(Column-1)*8+i] = OLED_F8x16[Char - ' '][i];//显示上半部分内容
		}
		for (i = 0; i < 8; i++)
		{
			OLED_Buffer[((Line ) * 2) - 1][(Column-1)*8+i]= OLED_F8x16[Char - ' '][i+8];	//显示下半部分内容
		}
}
/**
  * @brief  快速的OLED显示字符串
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串,范围:ASCII可见字符
  * @retval 无
  */
void OLED_FastShowString(uint8_t Line, uint8_t Column, char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i++)
	{
		OLED_FastShowChar(Line, Column + i, String[i]);
	}
}
/**
  * @brief  快速的OLED显示数字
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的字数字,范围:数字,变量
  * @param  Length 数字长度
  * @retval 无
  */
void OLED_FastShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)
	{
		OLED_FastShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}
/*
 * @brief 向OLED屏幕发送缓存区数据到OLED的0x40寄存器位置用以显示;
 */
void Refresh()
{
	uint8_t j;
	 uint8_t *pData;

	for(j=0;j<8;j++)
	{
		pData = &OLED_Buffer[j][0];
		OLED_SetCursor(j,0);//开始显示
		HAL_I2C_Mem_Write(&Hardware_IIC_No,0x78,0x40,I2C_MEMADD_SIZE_8BIT,pData,128,0xff);
	}
}
/**
  * @brief  OLED初始化
  * @param  无
  * @retval 无
  */
void OLED_Init(void)
{
	OLED_CS_1;
	OLED_DC_1;
	OLED_RST_0;
	HAL_Delay(400);
	OLED_CS_0;
	OLED_DC_0;
	OLED_RST_1;	
	HAL_Delay(400);
	OLED_WriteCommand(0xAE);	//关闭显示

	OLED_WriteCommand(0xD5);	//设置显示时钟分频比/振荡器频率
	OLED_WriteCommand(0x80);

	OLED_WriteCommand(0xA8);	//设置多路复用率
	OLED_WriteCommand(0x3F);

	OLED_WriteCommand(0xD3);	//设置显示偏移
	OLED_WriteCommand(0x00);

	OLED_WriteCommand(0x40);	//设置显示开始行

	OLED_WriteCommand(0xA1);	//设置左右方向,0xA1正常 0xA0左右反置

	OLED_WriteCommand(0xC8);	//设置上下方向,0xC8正常 0xC0上下反置

	OLED_WriteCommand(0xDA);	//设置COM引脚硬件配置
	OLED_WriteCommand(0x12);

	OLED_WriteCommand(0x81);	//设置对比度控制
	OLED_WriteCommand(0xCF);

	OLED_WriteCommand(0xD9);	//设置预充电周期
	OLED_WriteCommand(0xF1);

	OLED_WriteCommand(0xDB);	//设置VCOMH取消选择级别
	OLED_WriteCommand(0x30);

	OLED_WriteCommand(0xA4);	//设置整个显示打开/关闭

	OLED_WriteCommand(0xA6);	//设置正常/倒转显示

	OLED_WriteCommand(0x8D);	//设置充电泵
	OLED_WriteCommand(0x14);

	OLED_WriteCommand(0xAF);	//开启显示

	OLED_Clear();				//OLED清屏
}

在文件p2p_server_app.c中,找到初始化蓝牙的部分,在初始化蓝牙的同时,初始化自己的温湿度传感器和OLED。

void P2P_SERVER_APP_Init(void)
{
  UNUSED(P2P_SERVER_APP_Context);
  P2P_SERVER_Init();

  /* USER CODE BEGIN Service1_APP_Init */
  UTIL_SEQ_RegTask(1U << CFG_TASK_SEND_NOTIF_ID, UTIL_SEQ_RFU, P2P_SERVER_Switch_c_SendNotification);
  if (!sht3x_init(&handle))
  {
    LOG_INFO_APP("SHT3x access failed.\n\r");
  }
	OLED_Init();
	//OLED_ShowString(1, 1, "Hello World!!!");//显示字符串
	
  /**
   * Initialize LedButton Service
   */
  P2P_SERVER_APP_Context.Switch_c_Notification_Status = Switch_c_NOTIFICATION_OFF;
  P2P_SERVER_APP_LED_BUTTON_context_Init();
  /* USER CODE END Service1_APP_Init */
  return;
}

上位机在启动后,连接上蓝牙,就会将PWM的值通过蓝牙传输给下位机,下位机在接收到上位机的请求后,设置PWM,并读取SHT30,并将温湿度信息,返还给上位机。

/* Functions Definition ------------------------------------------------------*/
void P2P_SERVER_Notification(P2P_SERVER_NotificationEvt_t *p_Notification)
{
  /* USER CODE BEGIN Service1_Notification_1 */
	char buf[6];
  /* USER CODE END Service1_Notification_1 */
  switch(p_Notification->EvtOpcode)
  {
    /* USER CODE BEGIN Service1_Notification_Service1_EvtOpcode */

    /* USER CODE END Service1_Notification_Service1_EvtOpcode */

    case P2P_SERVER_LED_C_READ_EVT:
      /* USER CODE BEGIN Service1Char1_READ_EVT */

      /* USER CODE END Service1Char1_READ_EVT */
      break;

    case P2P_SERVER_LED_C_WRITE_NO_RESP_EVT:
      /* USER CODE BEGIN Service1Char1_WRITE_NO_RESP_EVT */
			LOG_INFO_APP("Receive Message   =%d  %d\n",p_Notification->DataTransfered.p_Payload[0],p_Notification->DataTransfered.p_Payload[1]);
			//从蓝牙接收到消息,pwm输出 
			__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, p_Notification->DataTransfered.p_Payload[0]*10); // 
			sprintf(buf,"%3d%%",p_Notification->DataTransfered.p_Payload[0]+1);
			OLED_ShowString( 2, 6,buf);
			P2P_SERVER_Switch_c_SendNotification();   
      /* USER CODE END Service1Char1_WRITE_NO_RESP_EVT */
      break;

    case P2P_SERVER_SWITCH_C_NOTIFY_ENABLED_EVT:
      /* USER CODE BEGIN Service1Char2_NOTIFY_ENABLED_EVT */
    P2P_SERVER_APP_Context.Switch_c_Notification_Status = Switch_c_NOTIFICATION_ON;
    LOG_INFO_APP("-- P2P APPLICATION SERVER : NOTIFICATION ENABLED\n");
    LOG_INFO_APP(" \n\r");
      /* USER CODE END Service1Char2_NOTIFY_ENABLED_EVT */
      break;

    case P2P_SERVER_SWITCH_C_NOTIFY_DISABLED_EVT:
      /* USER CODE BEGIN Service1Char2_NOTIFY_DISABLED_EVT */
    P2P_SERVER_APP_Context.Switch_c_Notification_Status = Switch_c_NOTIFICATION_OFF;
    LOG_INFO_APP("-- P2P APPLICATION SERVER : NOTIFICATION DISABLED\n");
    LOG_INFO_APP(" \n\r");
      /* USER CODE END Service1Char2_NOTIFY_DISABLED_EVT */
      break;

    default:
      /* USER CODE BEGIN Service1_Notification_default */

      /* USER CODE END Service1_Notification_default */
      break;
  }
  /* USER CODE BEGIN Service1_Notification_2 */

  /* USER CODE END Service1_Notification_2 */
  return;
}

、上位机编程。

上位机,这里使用的是python+pyqt。参考着老师的程序,写个个简单的界面。代码没有彻底完成,断开蓝牙的功能还没有实现。

c479273d7bcb39f173af54d150a70bc.png

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @FileName  :bt_pwm_light_mainwin.py
# @Time      :2024/12/24 15:25
# @Author    :aramy
import sys

from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QMainWindow, QApplication

from UI.btappwin import Ui_MainWindow
from bt_tool.bluetoothreceicer import BluetoothReceiver
from bt_tool.bluetoothscanner import BluetoothScanner


class MyMainForm(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.ui.dialPWM.setEnabled(False)
        self.ui.comboBoxBluetooth.setEnabled(False)

    @pyqtSlot(int)
    def on_dialPWM_valueChanged(self, value):
        self.ui.lineEditPWM.setText(str(self.ui.dialPWM.value()+1))
        self.thread1.pwmval = self.ui.dialPWM.value()

    @pyqtSlot()
    def on_pushButtonAction_clicked(self):
        if self.ui.pushButtonAction.text().find("扫描蓝牙")>=0:
            # 等待扫描蓝牙
            self.ui.comboBoxBluetooth.clear()
            self.ui.labeMsg.setText("正在扫描蓝牙设备...")
            async_thread = BluetoothScanner(self)
            async_thread.finished.connect(self.async_task_finished)
            async_thread.device_found_signal.connect(self.add_device_to_list)
            async_thread.start()
            self.ui.pushButtonAction.setEnabled(False)
        elif  self.ui.pushButtonAction.text().find("连接蓝牙")>=0:
            print("连接蓝牙")
            selected_item = self.ui.comboBoxBluetooth.currentText()
            self.ui.comboBoxBluetooth.setEnabled(False)
            if selected_item:
                device_address = selected_item.split('||')[1]
                self.ui.labeMsg.setText("正在连接蓝牙"+ selected_item)
                print(device_address)
                self.thread1 = BluetoothReceiver(device_address)
                self.thread1.finished.connect(self.async_task_finished)
                self.thread1.connection_status_signal.connect(self.update_status_label)
                self.thread1.data_received_signal.connect(self.update_data_label)
                self.thread1.start()
        elif self.ui.pushButtonAction.text().find("断开蓝牙")>=0:
            print("断开蓝牙")



    def update_data_label(self, data):
        try:
            # 接收到蓝牙上传的 温湿度信息
            self.ui.labeMsg.setText(f"接收数据: {data}")
            self.ui.lcdNumberTempture.display(data['Temp'])
            self.ui.lcdNumberHumid.display(data['Hum'])
        except Exception as e:
            print("接收数据解释错误:", str(e))

    def update_status_label(self, status):
        self.ui.labeMsg.setText(f"状态: {status}")
        self.ui.dialPWM.setEnabled(True)
        self.thread1.pwmval = self.ui.dialPWM.value()
        self.ui.comboBoxBluetooth.setEnabled(False)
        self.ui.pushButtonAction.setText("断开蓝牙")

    def async_task_finished(self):
        self.ui.labeMsg.setText("扫描完成")

    def add_device_to_list(self, device_info):
        # 查询到蓝牙列表,添加到combobox列表里
        if device_info['name'] is not None:
            print(device_info['name'],device_info['address'])
            self.ui.comboBoxBluetooth.addItem(device_info['name']+"||"+device_info['address'])
            self.ui.comboBoxBluetooth.setEnabled(True)
        self.ui.pushButtonAction.setText("连接蓝牙")
        self.ui.pushButtonAction.setEnabled(True)




if __name__ == "__main__":
    app = QApplication(sys.argv)    # 初始化
    myWin = MyMainForm()    # 将窗口控件显示在屏幕上
    myWin.show()    # 程序运行,sys.exit方法确保程序完整退出。
    sys.exit(app.exec_())

五、效果演示。

上位机启动,扫描蓝牙设备。

34b025fe2c520dfbc7aaa2911e91779.png

搜索到蓝牙设备后,连接蓝牙设备。

eed109c7d1f661b47a56d70e81c562f.png

连接上后,就可以读取温湿度信息,并且设置PWM的值了。

c479273d7bcb39f173af54d150a70bc.png

a1be2390a7498a29dbc35abb144137f.png

调试过程中,没有接LED灯管,使用示波器查看PWM波形。

97326afd36a1522e2f306c5bf856d07.jpg

image.png

六、源码

https://passport.eepw.com.cn/deal/down/id/394517

https://share.eepw.com.cn/share/download/id/394518

七、视频

https://www.bilibili.com/video/BV19drmYNEwX/







高工
2025-01-06 18:12:56     打赏
2楼

666666


助工
2025-01-06 18:59:19     打赏
3楼

感谢分享


共3条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]