【实现功能】
冬天来了,取暖器是家庭、工作最常用的取暖工具,传统的取暖器,只有几个固定的档位,大多以手工调节居多,这次我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系列提供了非常完友好的教程,结合示例,我们可以快速的创建自己的应用。