【实现功能】
冬天来了,取暖器是家庭、工作最常用的取暖工具,传统的取暖器,只有几个固定的档位,大多以手工调节居多,这次我DIY一个智能蓝牙控制的取暖器,使用STM32WBA55CG开发板做为主控制,可以实现对取暖器的远程控制,同时也可以监控室温,可以设计多个档位的设置等等好玩的功能。第一步是驱动Sht3x来检测室温,一来可以给用户了解室温,二来,可以象空调一样,进行精准的温度控制。
【软件准备】
【STM32WBA55CG开发板】1、开发资料分享-电子产品世界论坛
在这一篇中,我列举了需要按装的软件,这次我使用STM32CubeMX+MDK来进行开发。
【STM32WBA55CG开发板】DIY蓝牙控制取暖器——1、基于Pythonbleak的蓝牙桌面温度控制设计-电子产品世界论坛
这一篇中,我设计的桌面与开发板进行蓝牙交互的程序。
【实现步骤】
1、复制一份STM32Cube_FW_WBA_V1.4.1\Projects\NUCLEO-WBA55CG\Applications\BLE\BLE_p2pServert工程到同级目录下,大家可以根据自己的爱好,重命名工程名称。
2、使用stm32CubeMX打开工程设计文件,从原理图中,我们找到Ardiuno标准接口的原理图,使用D15与D14来连接Sht30温湿度传感器:

从原理图看DA15、D14接到了STM32WB55CG的PB2、PB1上,使用stm32CubeMX配置为I2C1,GPIO选择为PB2、PB1,同时选择通读速度为400K。

生成工程后使用MDK打开工程。
2、编译sht3x驱动库,分另为sht3x.c:
#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);
}
}sht3x.h:
#pragma once
#include <stdbool.h>
#if (defined STM32L011xx) || (defined STM32L021xx) || \
(defined STM32L031xx) || (defined STM32L041xx) || \
(defined STM32L051xx) || (defined STM32L052xx) || (defined STM32L053xx) || \
(defined STM32L061xx) || (defined STM32L062xx) || (defined STM32L063xx) || \
(defined STM32L071xx) || (defined STM32L072xx) || (defined STM32L073xx) || \
(defined STM32L081xx) || (defined STM32L082xx) || (defined STM32L083xx)
#include "stm32l0xx_hal.h"
#elif defined (STM32L412xx) || defined (STM32L422xx) || \
defined (STM32L431xx) || (defined STM32L432xx) || defined (STM32L433xx) || defined (STM32L442xx) || defined (STM32L443xx) || \
defined (STM32L451xx) || defined (STM32L452xx) || defined (STM32L462xx) || \
defined (STM32L471xx) || defined (STM32L475xx) || defined (STM32L476xx) || defined (STM32L485xx) || defined (STM32L486xx) || \
defined (STM32L496xx) || defined (STM32L4A6xx) || \
defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
#include "stm32l4xx_hal.h"
#else
#include "stm32wbaxx_hal.h"
#endif
#ifndef SHT3X_I2C_TIMEOUT
#define SHT3X_I2C_TIMEOUT 30
#endif
#define SHT3X_I2C_DEVICE_ADDRESS_ADDR_PIN_LOW 0x44
#define SHT3X_I2C_DEVICE_ADDRESS_ADDR_PIN_HIGH 0x45
/**
* Structure defining a handle describing a SHT3x device.
*/
typedef struct {
/**
* The handle to the I2C bus for the device.
*/
I2C_HandleTypeDef *i2c_handle;
/**
* The I2C device address.
* @see{PCA9865_I2C_DEVICE_ADDRESS_ADDR_PIN_LOW} and @see{SHT3X_I2C_DEVICE_ADDRESS_ADDR_PIN_HIGH}
*/
uint16_t device_address;
} sht3x_handle_t;
/**
* Checks if an SHT3x is reachable using the given handle.
* @param handle Handle to the SHT3x device.
* @return True on success, false otherwise.
*/
bool sht3x_init(sht3x_handle_t *handle);
/**
* Takes a single temperature and humidity measurement.
* @param handle Handle to the SHT3x device.
* @param temperature Pointer to the storage location for the sampled temperature.
* @param humidity Pointer to the storage location for the sampled humidity.
* @return True on success, false otherwise.
*/
bool sht3x_read_temperature_and_humidity(sht3x_handle_t *handle, float *temperature, float *humidity);
/**
* Turns the SHT3x's internal heater on or of.
* @param handle Handle to the SHT3x device.
* @param enable True to enable to heater, false to disable it.
* @return True on success, false otherwise.
*/
bool sht3x_set_header_enable(sht3x_handle_t *handle, bool enable);3、用户代码,首先打开p2p_server_app.c,添加sht3x的结构体声明:
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stm32wbaxx_nucleo.h"
#include "sht3x.h"
extern I2C_HandleTypeDef hi2c1;
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef struct{
uint8_t Device_Led_Selection;
uint8_t Led1;
}P2P_LedCharValue_t;
typedef struct{
uint8_t Device_Button_Selection;
uint8_t ButtonStatus;
}P2P_ButtonCharValue_t;
sht3x_handle_t handle = {
.i2c_handle = &hi2c1,
.device_address = SHT3X_I2C_DEVICE_ADDRESS_ADDR_PIN_LOW
};
/* USER CODE END PTD */4、在p2p服务初始化时添加sht3x的初始代码:
void P2P_SERVER_APP_Init(void)
{
UNUSED(P2P_SERVER_APP_Context);
P2P_SERVER_Init();
/* USER CODE BEGIN Service1_APP_Init */
// Initialise sensor (tests connection by reading the status register).
if (!sht3x_init(&handle)) {
LOG_INFO_APP("SHT3x access failed.\n\r");
}
UTIL_SEQ_RegTask( 1U << CFG_TASK_SEND_NOTIF_ID, UTIL_SEQ_RFU, P2P_SERVER_Switch_c_SendNotification);
/**
* 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;
}5、在数据上报的程序段,添加获取温湿度的代码,并分解为单位数据,组装好后发给上位机:
/*************************************************************
*
* LOCAL FUNCTIONS
*
*************************************************************/
__USED void P2P_SERVER_Switch_c_SendNotification(void) /* Property Notification */
{
P2P_SERVER_APP_SendInformation_t notification_on_off = Switch_c_NOTIFICATION_OFF;
P2P_SERVER_Data_t p2p_server_notification_data;
p2p_server_notification_data.p_Payload = (uint8_t*)a_P2P_SERVER_UpdateCharData;
p2p_server_notification_data.Length = 0;
/* USER CODE BEGIN Service1Char2_NS_1*/
float temperature, humidity;
volatile uint32_t value1,value2;
sht3x_read_temperature_and_humidity(&handle, &temperature, &humidity);
LOG_INFO_APP("Initial temperature: %.2fC, humidity: %.2f%%RH\n\r", temperature, humidity);
if(P2P_SERVER_APP_Context.ButtonControl.ButtonStatus == 0x00)
{
P2P_SERVER_APP_Context.ButtonControl.ButtonStatus = 0x01;
}
else
{
P2P_SERVER_APP_Context.ButtonControl.ButtonStatus = 0x00;
}
value1 = *(uint32_t *)&temperature;
value2 = *(uint32_t *)&humidity;
// a_P2P_SERVER_UpdateCharData[0] = (((int)temperature)/100%10); /* Device Led selection */
a_P2P_SERVER_UpdateCharData[0] = (((int)temperature)/10%10);
a_P2P_SERVER_UpdateCharData[1] = (((int)temperature)%10);
a_P2P_SERVER_UpdateCharData[2] = (((int)humidity)/10%10);
a_P2P_SERVER_UpdateCharData[3] = (((int)humidity)%10);
/* Update notification data length */
p2p_server_notification_data.Length = (p2p_server_notification_data.Length) + 4;
if(P2P_SERVER_APP_Context.Switch_c_Notification_Status == Switch_c_NOTIFICATION_ON)
{
LOG_INFO_APP("-- P2P APPLICATION SERVER : INFORM CLIENT BUTTON 1 PUSHED\n");
notification_on_off = Switch_c_NOTIFICATION_ON;
}
else
{
LOG_INFO_APP("-- P2P APPLICATION SERVER : CAN'T INFORM CLIENT - NOTIFICATION DISABLED\n");
}
/* USER CODE END Service1Char2_NS_1*/
if (notification_on_off != Switch_c_NOTIFICATION_OFF)
{
P2P_SERVER_UpdateValue(P2P_SERVER_SWITCH_C, &p2p_server_notification_data);
}
/* USER CODE BEGIN Service1Char2_NS_Last*/
/* USER CODE END Service1Char2_NS_Last*/
return;
}6、在蓝牙数据接收回调中,添加温度获取,同时调用发送任务:
void P2P_SERVER_Notification(P2P_SERVER_NotificationEvt_t *p_Notification)
{
/* USER CODE BEGIN Service1_Notification_1 */
/* 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 */
if(p_Notification->DataTransfered.p_Payload[1] == 0x01)
{
BSP_LED_On(LED_BLUE);
LOG_INFO_APP("-- P2P APPLICATION SERVER : LED1 ON\n");
P2P_SERVER_APP_Context.LedControl.Led1 = 0x01; /* LED1 ON */
P2P_SERVER_Switch_c_SendNotification();
}
if(p_Notification->DataTransfered.p_Payload[1] == 0x00)
{
BSP_LED_Off(LED_BLUE);
LOG_INFO_APP("-- P2P APPLICATION SERVER : LED1 OFF\n");
P2P_SERVER_APP_Context.LedControl.Led1 = 0x00; /* LED1 OFF */
}
/* 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;编译后下载给开发板,打开桌面程序,就可以实时实现温湿度的数据展示了。

【小结】
STM32WBA系列提供了非常完友好的教程,结合示例,我们可以快速的创建自己的应用。
我要赚赏金
