Skip to content

Contributed by sujingliang, organized by Ai-Thinker

[Ai-WB2 Review] BLE+PWM RGB LED Control

1. RGB LED Control APP Found an app that can control RGB LEDs over BLE

Its BLE RGB control command format is as follows:

0x4e0x4f0x300x40xc50x00xff0xff0xf1
Fixed formatFixed formatFixed formatFixed formatREDGREENBLUEALPHAUnknown

The command is 9 bytes in total The first four bytes are fixed at 0x4e 0x4f 0x30 0x4, and can be used to determine whether the following data is an RGB control command Byte 5 is RED Byte 6 is GREEN Byte 7 is BLUE Byte 8 is ALPHA Byte 9 is not understood and is not used

2. Writing the BLE Slave Program Use applications/bluetooth/ble_slave as the template.

  1. Add PWM RGB LED control code pwmled.h
Click to expand full code
java
#ifndef __PWM_LED_H__
#define __PWM_LED_H__

// pwmled.h

#include <bl602.h>
#include <bl602_gpio.h>
#include <bl_pwm.h>

BL_Err_Type
PWM_Smart_Configure2
(PWM_CH_ID_Type ch, uint16_t clkDiv, uint16_t period,uint16_t threshold2)
;

void

pwm_led_init
(
void
)
;

void

pwm_led_set
(uint8_t red,uint8_t green,uint8_t blue)
;

void

rgb_control
(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
;
#endif

BL_Err_Type PWM_Smart_Configure2(PWM_CH_ID_Type ch, uint16_t clkDiv, uint16_t period,uint16_t threshold2); Configure PWM parameters

void pwm_led_init(void); Initialize PWM

void pwm_led_set(uint8_t red,uint8_t green,uint8_t blue); Set the 3 RGB LEDs

void rgb_control(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha); Control the 3 RGB LEDs with alpha added

pwmled.cfunction implementation

Click to expand full code
java
#include <FreeRTOS.h>
#include <task.h>
#include <blog.h>
#include <bl602.h>
#include <bl602_gpio.h>
#include <bl602_glb.h>
#include <bl_pwm.h>
#include
"pwmled.h"

#define
PWM_Get_Channel_Reg
(ch)
  (PWM_BASE+PWM_CHANNEL_OFFSET+(ch)*
0x20
)
#define
PWM_STOP_TIMEOUT_COUNT

(
160
*
1000
)

GLB_GPIO_Cfg_Type cfg[
3
] = {
    {
        .drive =
0
,
        .smtCtrl =
1
,
        .gpioMode = GPIO_MODE_OUTPUT,
        .pullType = GPIO_PULL_DOWN,
        .gpioPin =
14
,
/// red

        .gpioFun =
8
,
    },
    {
        .drive =
0
,
        .smtCtrl =
1
,
        .gpioMode = GPIO_MODE_OUTPUT,
        .pullType = GPIO_PULL_DOWN,
        .gpioPin =
17
,
/// green

        .gpioFun =
8
,
    },
    {
        .drive =
0
,
        .smtCtrl =
1
,
        .gpioMode = GPIO_MODE_OUTPUT,
        .pullType = GPIO_PULL_DOWN,
        .gpioPin =
3
,
/// blue

        .gpioFun =
8
,
    },
};

/// @brief Copy from PWM_Smart_Configure. Use Bus Clock instead of External Crystal Clock for PWM Timer

/// @param ch PWM Channel

/// @param clkDiv PWM clock divider

/// @param period PWM period

/// @param threshold2

/// @return

BL_Err_Type
PWM_Smart_Configure2
(PWM_CH_ID_Type ch, uint16_t clkDiv, uint16_t period,uint16_t threshold2)

{
    uint32_t tmpVal;

uint32_t

timeoutCnt

=
 PWM_STOP_TIMEOUT_COUNT;

/* Get channel register */

uint32_t

PWMx

=
 PWM_Get_Channel_Reg(ch);

    tmpVal = BL_RD_REG(PWMx, PWM_CONFIG);

// if(BL_GET_REG_BITS_VAL(tmpVal, PWM_REG_CLK_SEL) != PWM_CLK_XCLK){

if
(BL_GET_REG_BITS_VAL(tmpVal, PWM_REG_CLK_SEL) != PWM_CLK_BCLK){
        BL_WR_REG(PWMx, PWM_CONFIG, BL_SET_REG_BIT(tmpVal, PWM_STOP_EN));

while
(!BL_IS_REG_BIT_SET(BL_RD_REG(PWMx, PWM_CONFIG), PWM_STS_TOP)){
            timeoutCnt--;

if
(timeoutCnt ==
0
){

return
 TIMEOUT;
            }
        }

// tmpVal = BL_SET_REG_BITS_VAL(tmpVal, PWM_REG_CLK_SEL, PWM_CLK_XCLK);

        tmpVal = BL_SET_REG_BITS_VAL(tmpVal, PWM_REG_CLK_SEL, PWM_CLK_BCLK);
    }
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, PWM_OUT_INV, PWM_POL_NORMAL);
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, PWM_STOP_MODE, PWM_STOP_GRACEFUL);
    BL_WR_REG(PWMx, PWM_CONFIG, tmpVal);

/* Config pwm division */

    BL_WR_REG(PWMx, PWM_CLKDIV, clkDiv);

/* Config pwm period and duty */

    BL_WR_REG(PWMx, PWM_PERIOD, period);
    BL_WR_REG(PWMx, PWM_THRE1,
0
);
    BL_WR_REG(PWMx, PWM_THRE2, threshold2);

return
 SUCCESS;
}

void

pwm_led_init
(
void
)

{

for
 (
int

i

=

0
; i <
3
; i++) {
        GLB_GPIO_Init(cfg + i);

PWM_CH_ID_Type

ch

=
 cfg[i].gpioPin % PWM_CH_MAX;
        PWM_Channel_Disable(ch);
    }
}

void

pwm_led_set
(uint8_t red,uint8_t green,uint8_t blue)

{
    PWM_Smart_Configure2(
2
,
80
,
255
, green);
    PWM_Channel_Enable(
2
);

    PWM_Smart_Configure2(
3
,
80
,
255
, blue);
    PWM_Channel_Enable(
3
);

    PWM_Smart_Configure2(
4
,
80
,
255
, red);
    PWM_Channel_Enable(
4
);

}

void

rgb_control
(uint8_t red, uint8_t green, uint8_t blue,  uint8_t alpha)

{
    uint8_t t_red,t_green,t_blue;

// 应用透明度/亮度

    t_red = red*alpha/
255
;
    t_green = green*alpha/
255
;
    t_blue = blue*alpha/
255
;

    pwm_led_set(t_red,t_green,t_blue);
}

2. Modify ble_interface.c as follows

Click to expand full code
java
static
 ssize_t
ble_uuid1_write_val
(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                   const
void
 *buf, u16_t len, u16_t offset,
                                   u8_t flags)

{
    uint8_t *recv_buffer;
    recv_buffer = pvPortMalloc(sizeof(uint8_t) * len);
    memcpy(recv_buffer, buf, len);
    printf(
"recv ble data len: %d\r\n"
, len);

if
(recv_buffer[
0
]==
0x4e
&&recv_buffer[
1
]==
0x4f
&&recv_buffer[
2
]==
0x30
&&recv_buffer[
3
]==
0x04
){

//pwm_led_set(recv_buffer[4],recv_buffer[5],recv_buffer[6]);

        rgb_control(recv_buffer[
4
],recv_buffer[
5
],recv_buffer[
6
],recv_buffer[
7
]);
        printf(
"pwm_led_set\r\n"
);
    }

for
 (
size_t

i

=

0
; i < len; i++)
    {
        printf(
"0x%x "
, recv_buffer[i]);
    }
    printf(
"\r\n"
);
    vPortFree(recv_buffer);

return
 len;
}

What was added is:

Click to expand full code
java
if
(recv_buffer[
0
]==
0x4e
&&recv_buffer[
1
]==
0x4f
&&recv_buffer[
2
]==
0x30
&&recv_buffer[
3
]==
0x04
){

//pwm_led_set(recv_buffer[4],recv_buffer[5],recv_buffer[6]);

        rgb_control(recv_buffer[
4
],recv_buffer[
5
],recv_buffer[
6
],recv_buffer[
7
]);
        printf(
"pwm_led_set\r\n"
);
    }

That is, when UUID1 receives a write value, it checks whether the first 4 bytes are 0x4e 0x4f 0x30 0x4, and if so, calls rgb_control to change the RGB LED color.

3. Running Run the APP on the phone

Scan for BLE and connect

Change the uuid to match the configuration

Once in the interface, it can be operated.

After operating, the serial output is:

Released under the MIT License. Build Time 2026-09-11 14:52:23