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

这里使用SHT30作为温湿度传感器,使用I2C与NUCEO-WBA55CG相连接;使用了一块微雪的1.51寸透明OLED屏幕,用来展示PWM的信息。这块OLED屏幕也是使用I2C驱动,透明的OLED看上去科技感很足。
二、硬件连接。
| CN4 | sht30 | OLED | ||||||||||||
| 2 | GPIO_2 | 1 | GPIO_26 | 2 | GPIO_25 | |||||||||
| 4 | GPIO_4 | 3 | GPIO_28 | PB2 | D15 | SCL | 黄 | 黄 | 4 | GPIO_27 | ||||
| 6 | 5V | 5 | GPIO_29 | PB1 | D14 | SDA | 绿 | 绿 | 6 | GPIO_30 | ||||
| 8 | GND | 7 | AVDD | AVDD | 8 | 5V | ||||||||
| 10 | 9 | GND | 10 | GPIO_32 | ||||||||||
| 12 | IOREF | 11 | GPIO_31 | PB4(LD1) | D13 | SCK | 12 | GPIO_35 | ||||||
| 14 | NRST | 13 | GPIO_33 | PB3 | D12 | MISO | 14 | GPIO_36 | ||||||
| 16 | 3V3 | 15 | GPIO_34 | PA15 | D11 | MOSI | 16 | GPIO_38 | ||||||
| 18 | 5V | 17 | GPIO_37 | PA12 | D10 | SS | 橙 CS | 18 | GPIO_40 | |||||
| 20 | GND | 19 | GPIO_39 | PA9 | D9 | 20 | GND | |||||||
| 22 | GND | 21 | GPIO_41 | PA15/PA11 | D8 | 22 | GPIO_43 | |||||||
| 24 | VIN | 23 | GPIO_42 | PB9/PC13 | D7 | 24 | GPIO_45 | |||||||
| 26 | GPIO_7 | 25 | GPIO_44 | PB0 | D6 | 白 RST | 26 | GPIO_46 | ||||||
| 28 | GPIO_11 | PA7 | A0 | 27 | GPIO_47 | PB14 | D5 | 绿 DC | 28 | GPIO_48 | ||||
| 30 | GPIO_12 | PA6 | A1 | TIM2_CH4 | PWM输出 | 29 | GPIO_49 | PB13 | D4 | 30 | GPIO_51 | |||
| 32 | GPIO_17 | PA2 | A2 | 31 | GPIO_50 | PB6(B2) | D3 | 32 | GND | |||||
| 34 | GPIO_18 | PA1 | A3 | 33 | GPIO_52 | PB7(B3) | D2 | 34 | GPIO_53 | |||||
| 36 | GPIO_21 | PA5/PA8 | A4 | SDA | 35 | GPIO_54 | PB5 | D1 | 36 | GPIO_56 | ||||
| 38 | GPIO_22 | PA0/PA5 | A5 | SCL | 37 | GPIO_55 | PA10 | D0 | 38 | GPIO_57 |

线接的比较乱,OLED和SHT30都是走I2C接口,其中OLED还有rst、dc、cs三条线,测试下来dc,cs两条线感觉是没有用到,可以悬空的。OLED也有SPI的接线方式,但是尝试过使用SPI去驱动OLED屏幕,在蓝牙的例程中,系统会出现卡死,暂时没能解决,所以最终选择了使用I2C方式驱动。
三、下位机编程。
参考着老师的视频,使用例程来作为基础工程,这里我使用了WBA1.5.0的例程。例程初次编译,会有一个报错,调整一下对应文件路径就可以解决了。



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

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

接下来编程部分,添加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。参考着老师的程序,写个个简单的界面。代码没有彻底完成,断开蓝牙的功能还没有实现。

#!/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_())五、效果演示。
上位机启动,扫描蓝牙设备。

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

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


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


六、源码
https://passport.eepw.com.cn/deal/down/id/394517
https://share.eepw.com.cn/share/download/id/394518
七、视频
https://www.bilibili.com/video/BV19drmYNEwX/
我要赚赏金
