基于安信可 PB-03 模组的温湿度彩灯开发教程

概述

安信可 PB-03 模组基于奉加微 PHY-6252 SoC 平台,支持 BLE、BLE Mesh,基于 Cortex-M0 内核,外挂 512 KB Flash。本篇文章介绍如何使用安信可 PB-03M-Kit 开发板搭配 SHT30 温湿度传感器实现一个带温湿度测量功能的 BLE Mesh 彩灯。

开发环境搭建

  • PHY62XX SDK 3.1.1
  • Keil μVision MDK-ARM Plus V5.26.2.0
  • PhyPlusKit v2.5.2c
  • Visual Studio Code

安装好上述三个软件,SDK 解压到任意不带中文路径的文件夹,用 Keil 打开release_bbb_sdk-PHY62XX_SDK_3.1.1\example\ble_mesh\mesh_light\mesh_light.uvprojx先编译一遍,验证环境是否搭建成功,随后使用 Visual Studio Code 打开release_bbb_sdk-PHY62XX_SDK_3.1.1文件夹,安装 C/C++ 相关扩展以启用自动补全功能,至此环境搭建完毕。

开发

官方的mesh_light项目其实已经实现了彩灯控制功能,因此我们只需要在mesh_light的基础上增加温湿度上报功能就可以了。不过 PB-03M Kit 的引脚与官方开发板有所不同,需要对原来的代码进行一些修改才能点亮彩灯。

修改彩灯的 GPIO 引脚

定位到example\ble_mesh\mesh_gateway\source\bleMesh\bleMesh.c,修改GPIO_REDGPIO_GREENGPIO_BLUE为 PB-03M Kit 对应的 RGB 彩灯引脚号(见 PB-03M-Kit 开发板规格书):

基于安信可 PB-03 模组的温湿度彩灯开发教程_第1张图片

#define GPIO_RED P7
#define GPIO_GREEN P11
#define GPIO_BLUE P18

修改 PWM 极性

定位到components\driver\led_light\led_light.c,根据实际电路中彩灯是高电平点亮还是低电平点亮修改 PWM 信号的极性。

void light_pwm_init(void)
{
    hal_pwm_module_init();

    for(int i = 0; i < sizeof(pwm_ch)/sizeof(pwm_ch[0]); i++)
    {
        pwm_ch[i].pwmN = (PWMN_e)(PWM_CH0 + (PWMN_e)i);
        pwm_ch[i].pwmPin = GPIO_DUMMY;
        pwm_ch[i].pwmDiv = PWM_CLK_NO_DIV;
        pwm_ch[i].pwmMode = PWM_CNT_UP;
        pwm_ch[i].pwmPolarity = PWM_POLARITY_FALLING; // 此处修改为 PWM_POLARITY_RISING
        pwm_ch[i].cmpVal = 0;
        pwm_ch[i].cntTopVal = LIGHT_TOP_VALUE;
    }

    pwm_ch[0].pwmPin = *(led_pin_ptr + 0);
    pwm_ch[1].pwmPin = *(led_pin_ptr + 1);
    pwm_ch[2].pwmPin = *(led_pin_ptr + 2);
}

使用 I²C 总线和 GPIO 从 SHT30 传感器中读取温湿度

SHT30 传感器通过 I²C 总线与模组进行通信,整体读取流程如下:

基于安信可 PB-03 模组的温湿度彩灯开发教程_第2张图片

我们每隔 500ms 通过 I²C 读取一次传感器数据,如果我们读取成功,则从传感器的原始数据计算真实的温湿度;如果读取失败,说明传感器可能死机,我们就要通过 GPIO 操作传感器复位脚模拟复位动作来复位传感器,等待 3s 后再次尝试读取传感器。

因为该流程涉及到状态的转换以及使用了定时器,我们使用一个 OSAL 任务来承载读取传感器的工作,以下是任务的主要代码:

uint16 sht30_ProcessEvent(uint8 task_id, uint16 events)
{
    if (events & SHT30_RST_PULL_DOWN_EVT)
    {
        // 按下重置按键
        hal_gpio_fast_write(P20, 0);
        // 300ms 后释放重置按键
        osal_start_timerEx(sht30_TaskID, SHT30_RST_PULL_UP_EVT, 300);
    }

    if (events & SHT30_RST_PULL_UP_EVT)
    {
        // 释放重置按键
        hal_gpio_fast_write(P20, 1);
        // 300ms 后发送读取指令
        osal_start_timerEx(sht30_TaskID, SHT30_SEND_READ_EVT, 300);
    }

    if (events & SHT30_SEND_READ_EVT)
    {
        // 初始化 I²C
        hal_i2c_pin_init(I2C_0, P24, P23);
        pi2cdev = hal_i2c_init(I2C_0, I2C_CLOCK_100K);
        // 发送读取指令
        write_command(SHT31_MEAS_HIGHREP);
        // 3s 后读取数据
        osal_start_timerEx(sht30_TaskID, SHT30_DATA_RECEIVE_EVT, 3000);
    }

    if (events & SHT30_DATA_RECEIVE_EVT)
    {
        uint8 buffer[6];
        osal_memset(buffer, 0, sizeof buffer);

        // 尝试从 I²C 总线读取数据
        int ret = hal_i2c_read(pi2cdev, SHT31_DEFAULT_ADDR, 0, buffer, sizeof buffer);
        if (ret == PPlus_ERR_TIMEOUT)
        {
            // 数据读取超时,开始发送重置指令
            LOG("i2c read timeout, reset sensor\r\n");
            osal_set_event(sht30_TaskID, SHT30_RST_PULL_DOWN_EVT);
        }
        else
        {
            // 将读取到的数据打印出来
            LOG("received data:");
            LOG_DUMP_BYTE(buffer, sizeof buffer);

						/// 处理数据,获得真正的温湿度并上报的代码省略

            // 500ms 后再次发送读取指令,进入下一轮循环
            osal_start_timerEx(sht30_TaskID, SHT30_SEND_READ_EVT, 500);
        }
    }

    // 丢弃未处理的事件
    return 0;
}

void sht30_Init(uint8 task_id)
{
    sht30_TaskID = task_id;

    // 初始化传感器重置引脚
    hal_gpio_pull_set(P20, GPIO_PULL_DOWN);
    hal_gpio_write(P20, 1);

    // 发送读取命令,开始进入工作循环
    osal_set_event(sht30_TaskID, SHT30_SEND_READ_EVT);
}

修改广播的设备名称

定位到example\ble_mesh\mesh_light\source\bleMesh\appl_sample_example_phylight.c中的UI_set_brr_scan_rsp_data()函数,修改广播的设备名称:

API_RESULT UI_set_brr_scan_rsp_data (void)
{
    /**
        Currently setting MT-MESH-SAMPLE-8 as Complete Device Name!
        This can be updated to each individual devices as per requirement.
    */
    UCHAR UI_brr_scanrsp_data[] =
    {
        0x0D, 0x09, 'P', 'H', 'Y', '-', 'M', 'S', 'H', 'L', 'I', 'G', 'H', 'T'
    };
    CONSOLE_OUT("\n Setting PHY MSH LIGHT as Complete Device Name!\n");
    /* Set the Scan Response Data at the Bearer Layer */
    blebrr_set_adv_scanrsp_data_pl
    (
        UI_brr_scanrsp_data,
        sizeof(UI_brr_scanrsp_data)
    );
    return API_SUCCESS;
}

此处的UI_br_scanrsp_data[]中的第二个字节0x09代表设备名称长度(不包含\0),修改设备名称时不要忘记更改这个地方。

修改 UUID

定位到example\ble_mesh\mesh_light\source\bleMesh\appl_sample_example_phylight.c,修改UI_lprov_device.uuid 即可修改广播的 UUID。

/** Unprovisioned device identifier */
//DECL_STATIC PROV_DEVICE_S UI_lprov_device =
PROV_DEVICE_S UI_lprov_device =
{
    /** UUID */
    {0x88, 0x88, 0x62, 0x12, 0x00, 0x01, 0x00, 0x01, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00},
    //0x05,0x04
    /** OOB Flag */
    0x00,

    /**
        Encoded URI Information
        For example, to give a web address, "https://www.abc.com"
        the URI encoded data would be -
        0x17 0x2F 0x2F 0x77 0x77 0x77 0x2E 0x61 0x62 0x63 0x2E 0x63 0x6F 0x6D
        where 0x17 is the URI encoding for https:
    */
    NULL
};

缩小固件体积

使用精简版printf()函数

SDK 中的myprintf.h头文件提供了一个精简版的printf()函数,这个函数相比于 Keil 提供的完整版的printf()函数少了浮点数打印功能,但是最后生成的代码体积相应也更小。如果项目业务部分代码量过大,使用完整版printf()函数可能会因为目标文件体积过大而链接失败。在不需要打印浮点数的项目中,我们应尽量使用精简版printf()函数缩小生成固件的体积。

在正式发布版本中关掉日志输出

SDK 项目的 Keil 编译选项中提供了DEBUG_INFO宏来定义日志输出级别。在log.h中可以看见,将DEBUG_INFO 宏设置为0 可以关掉日志输出,设置为3可以打开全部的日志输出。

#if(DEBUG_INFO == 1)
#define AT_LOG(...)
#define LOG_DEBUG(...)
#define LOG(...)  dbg_printf(__VA_ARGS__)
#define LOG_INIT() dbg_printf_init()
#define LOG_DUMP_BYTE(a,b) my_dump_byte(a,b)
#elif(DEBUG_INFO == 2)

#define AT_LOG(...)  dbg_printf(__VA_ARGS__)
#define LOG_DEBUG(...)  dbg_printf(__VA_ARGS__)
#define LOG(...)
#define LOG_INIT() dbg_printf_init()
#define LOG_DUMP_BYTE(a,b) my_dump_byte(a,b)
#elif(DEBUG_INFO == 3)
#define LOG(...)  dbg_printf(__VA_ARGS__)
#define AT_LOG(...)  dbg_printf(__VA_ARGS__)
#define LOG_DEBUG(...)  dbg_printf(__VA_ARGS__)
#define LOG_INIT() dbg_printf_init()
#define LOG_DUMP_BYTE(a,b) my_dump_byte(a,b)
#else
#define AT_LOG(...)
#define LOG_DEBUG(...)
#define LOG(...)
#define LOG_INIT()  //{clk_gate_enable(MOD_UART);clk_reset(MOD_UART);clk_gate_disable(MOD_UART);}
#define LOG_DUMP_BYTE(a,b)
#endif

注释掉不需要的代码

SDK 的项目中自带一个简单的命令行演示程序,用来做显示 Mesh 配网信息和重置模组等操作。这个命令行占用的代码体积较大,业务逻辑过多的情况下很容易超体积,我们可以删除掉引入的命令行组件来减小代码体积。

群控测试

我们使用 nRF Mesh 这款 App 测试 Mesh 群控功能。打开 App ,添加几个设备进行测试。然后创建群组并选择设备加入,测试开关灯功能是否正常。

代码链接

https://github.com/zxf1023818103/release_bbb_sdk-PHY62XX_SDK_3.1.1.git

你可能感兴趣的:(单片机,嵌入式硬件)