这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » 【STM32WBA55CG开发板】DIY蓝牙控制取暖器——2、移植Sht3x驱动

共3条 1/1 1 跳转至

【STM32WBA55CG开发板】DIY蓝牙控制取暖器——2、移植Sht3x驱动,并实现温度上报

助工
2024-11-11 11:24:19     打赏

【实现功能】

冬天来了,取暖器是家庭、工作最常用的取暖工具,传统的取暖器,只有几个固定的档位,大多以手工调节居多,这次我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温湿度传感器:

image.png

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

image.png

生成工程后使用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;

编译后下载给开发板,打开桌面程序,就可以实时实现温湿度的数据展示了。

image.png

【小结】

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




关键词: STM32WBA55CG     蓝牙     取暖器     Sht3x    

院士
2024-11-15 19:50:06     打赏
2楼

谢谢分享的桌面与开发板进行蓝牙交互的程序。


院士
2024-11-17 08:28:52     打赏
3楼

谢谢楼主的无私分享~!


共3条 1/1 1 跳转至

回复

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