[i=s] 本帖最后由 无垠的广袤 于 2025-6-23 17:27 编辑 [/i]
【电子DIY作品】BW21-CBV-Kit 开发套件扩展板设计
本文介绍了安信可 BW21-CBV-Kit 开发套件 扩展板 及其 外壳 的项目设计。
项目介绍
主控为 安信可 BW21 模组 开发板,得益于该模组的 强劲性能 与 完善的生态建设 ,可实现诸如
等 AI 相关项目,为智能设备和产品的快速开发和部署提供了极大便利。
外观

扩展板工程详见:智能交互与识别机器人 - 立创开源硬件平台 .
核心板工程详见:BW21-CBV-Kit Ai视觉识别开发板 - 立创开源硬件平台 .
参数特点
- 所有模组 IO 引出,便于外设连接与开发调试;
- 增加多组 5V 、3.3V 和 GND 扩展引脚,为外设开发提供便利;
- 增加 TP4057 充电管理电路,支持 3.7V 锂电池供电输入;
- 充电管理包括充电指示灯与 XH2.54 电源插接母座,模块化设计,便于快速组装测试;
- 包括 DHT11 单总线传感器接口,信号引脚带上拉电阻,可直插传感器或模块;
- IIC 接口,可连接 OLED 显示屏;
- SPI 通信接口,可连接 TFT 彩色显示屏;
功能定义
包括排针扩展接口、电源输出扩展、充电指示灯、状态指示灯、XH2.54 电池供电接口、DHT11传感器接口、IIC OLED接口、SPI/TFT屏接口等。

原理图

3D外壳
- 四角 M3 螺丝定位孔设计,便于 DIY 安装与拆卸;
- 扩展排针和电源排针设计,便于外部模块连接;
- TFT 和 OLED 屏排母接口设计,便于工程快速验证与显示测试;
- BW21 核心板模组位置下方挖槽,增强散热;
- 天线位置预留凹槽设计,便于天线延展,使无线通信更加畅通无阻;
- 侧边精准挖槽,便于Type-C充电接口连接,充电指示灯显示,以及 DHT11 模块接入;

实物展示

功能测试
包括扩展板和核心板的板载资源测试,如 OLED 单色屏、TFT 彩屏、DHT11 传感器等。
OLED 显示
介绍了 IIC 通信的 SSD1306 驱动 OLED 屏显示。

根据原理图,SCL 和 SDA 分别对应 E3 (13)和 E4(12) 引脚。
测试代码
新建 Arduino 工程,添加如下代码
#include <Wire.h>
#include <Adafruit_OLED_libraries/Adafruit_GFX.h>
#include <Adafruit_OLED_libraries/Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define NUMFLAKES 10 // Number of snowflakes in the animation example
#define LOGO_HEIGHT_FLAKES 16
#define LOGO_WIDTH_FLAKES 16
static const unsigned char PROGMEM logo_bmp_flake[] =
{0b00000000, 0b11000000,
0b00000001, 0b11000000,
0b00000001, 0b11000000,
0b00000011, 0b11100000,
0b11110011, 0b11100000,
0b11111110, 0b11111000,
0b01111110, 0b11111111,
0b00110011, 0b10011111,
0b00011111, 0b11111100,
0b00001101, 0b01110000,
0b00011011, 0b10100000,
0b00111111, 0b11100000,
0b00111111, 0b11110000,
0b01111100, 0b11110000,
0b01110000, 0b01110000,
0b00000000, 0b00110000};
void setup()
{
Serial.begin(115200);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;)
; // Don't proceed, loop forever
}
// Clear the buffer
display.clearDisplay();
// Show the display buffer on the screen. You MUST call display() after
// drawing commands to make them visible on screen!
display.display();
delay(500);
testanimate(logo_bmp_flake, LOGO_WIDTH_FLAKES, LOGO_HEIGHT_FLAKES); // Animate bitmaps
}
void loop()
{
}
#define XPOS 0 // Indexes into the 'icons' array in function below
#define YPOS 1
#define DELTAY 2
void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h)
{
int8_t f, icons[NUMFLAKES][3];
// Initialize 'snowflake' positions
for (f = 0; f < NUMFLAKES; f++) {
icons[f][XPOS] = random(1 - LOGO_WIDTH_FLAKES, display.width());
icons[f][YPOS] = -LOGO_HEIGHT_FLAKES;
icons[f][DELTAY] = random(1, 6);
Serial.print(F("x: "));
Serial.print(icons[f][XPOS], DEC);
Serial.print(F(" y: "));
Serial.print(icons[f][YPOS], DEC);
Serial.print(F(" dy: "));
Serial.println(icons[f][DELTAY], DEC);
}
for (;;) { // Loop forever...
display.clearDisplay(); // Clear the display buffer
// Draw each snowflake:
for (f = 0; f < NUMFLAKES; f++) {
display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SSD1306_WHITE);
}
display.display(); // Show the display buffer on the screen
delay(200); // Pause for 1/10 second
// Then update coordinates of each flake...
for (f = 0; f < NUMFLAKES; f++) {
icons[f][YPOS] += icons[f][DELTAY];
// If snowflake is off the bottom of the screen...
if (icons[f][YPOS] >= display.height()) {
// Reinitialize to a random position, just off the top
icons[f][XPOS] = random(1 - LOGO_WIDTH_FLAKES, display.width());
icons[f][YPOS] = -LOGO_HEIGHT_FLAKES;
icons[f][DELTAY] = random(1, 6);
}
}
}
}
效果

SPI 显示
介绍了 SPI 通信的 SSD1306 驱动 OLED 屏显示图片和文字。

根据原理图,SPI 通信 OLED 引脚对应关系如下
SPI OLED |
BW21 |
SCL |
E3 (13) |
SDA |
E4(12) |
RST |
F8(5) |
DC |
F11(4) |
CS |
F12(3) |
将目标图片尺寸调整至合适大小,转换为 XBM格式(单色位图)
在线 XBM 转化工具:XBM Converter .
测试代码
新建 Arduino 工程,添加如下代码
这里使用了 U8g2 库,注意引脚定义
#include <U8g2lib.h>
#include <SPI.h>
#include "logo.h"
// 初始化 U8g2(SPI 模式)
U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(
U8G2_R0,
/* clock=*/ 13, // SCK
/* data=*/ 12, // MOSI (SDA)
/* cs=*/ 3, // Chip Select
/* dc=*/ 4, // Data/Command
/* reset=*/ 5 // RESET
);
void setup() {
u8g2.begin();
u8g2.setFont(u8g2_font_helvB10_tr);
}
void loop() {
u8g2.clearBuffer();
// 1. 显示文字(上方区域,128x19像素)
u8g2.setCursor(0, 12); // 文字起始位置(x,y)
u8g2.print("Hello, Ai-Thinker"); // 要显示的文字
// 2. 显示图片(下方区域,128x45像素)
u8g2.drawXBMP(0, 19, logo_width, logo_height, logo);
u8g2.sendBuffer();// 刷新显示
delay(2000);
}
上传代码至开发板,复位运行。
效果

DHT11传感器

详见:DHT11 模块 - 立创开源硬件平台 .
根据原理图可知,模块数据引脚对应 F5(0)引脚。
测试代码
新建 Arduino 工程,添加如下代码
#include "BLEDevice.h"
#include "DHT.h"
/* OLED */
#include <Wire.h>
#include <Adafruit_OLED_libraries/Adafruit_GFX.h>
#include <Adafruit_OLED_libraries/Adafruit_SSD1306.h>
#include <ArduinoJson.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define UART_SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
#define STRING_BUF_SIZE 100
// The digital pin we're connected to.
#define DHTPIN 0
// Uncomment whatever type you're using!
#define DHTTYPE DHT11 // DHT 11
// #define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
// #define DHTTYPE DHT21 // DHT 21 (AM2301)
DHT dht(DHTPIN, DHTTYPE);
BLEService UartService(UART_SERVICE_UUID);
BLECharacteristic Rx(CHARACTERISTIC_UUID_RX);
BLECharacteristic Tx(CHARACTERISTIC_UUID_TX);
BLEAdvertData advdata;
BLEAdvertData scndata;
bool notify = false;
void writeCB(BLECharacteristic* chr, uint8_t connID)
{
printf("Characteristic %s write by connection %d :\n", chr->getUUID().str(), connID);
if (chr->getDataLen() > 0) {
Serial.print("Received string: ");
Serial.print(chr->readString());
Serial.println();
}
}
void notifCB(BLECharacteristic* chr, uint8_t connID, uint16_t cccd)
{
if (cccd & GATT_CLIENT_CHAR_CONFIG_NOTIFY) {
printf("Notifications enabled on Characteristic %s for connection %d \n", chr->getUUID().str(), connID);
notify = true;
} else {
printf("Notifications disabled on Characteristic %s for connection %d \n", chr->getUUID().str(), connID);
notify = false;
}
}
void setup()
{
Serial.begin(115200);
advdata.addFlags();
advdata.addCompleteName("AMEBA_BLE_DEV");
scndata.addCompleteServices(BLEUUID(UART_SERVICE_UUID));
Rx.setWriteProperty(true);
Rx.setWritePermissions(GATT_PERM_WRITE);
Rx.setWriteCallback(writeCB);
Rx.setBufferLen(STRING_BUF_SIZE);
Tx.setReadProperty(true);
Tx.setReadPermissions(GATT_PERM_READ);
Tx.setNotifyProperty(true);
Tx.setCCCDCallback(notifCB);
Tx.setBufferLen(STRING_BUF_SIZE);
UartService.addCharacteristic(Rx);
UartService.addCharacteristic(Tx);
BLE.init();
BLE.configAdvert()->setAdvData(advdata);
BLE.configAdvert()->setScanRspData(scndata);
BLE.configServer(1);
BLE.addService(UartService);
BLE.beginPeripheral();
dht.begin();
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;)
; // Don't proceed, loop forever
}
delay(1000);
display.clearDisplay();
display.setTextColor(WHITE);
}
void loop()
{
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
// Create JSON payload
DynamicJsonDocument doc(256);
doc["temperature"] = String(t,2);
doc["humidity"] = String(h,2);
char json_string[256];
serializeJson(doc, json_string);
Serial.print("Publishing: ");
Serial.println(json_string);
//String msg = ("Humidity: " + String((int)h) + "%\t" + "Temperature: " + String((int)t) + "°C\n");
String msg = (json_string);
Tx.writeString(msg);
if (BLE.connected(0) && notify) {
Tx.notify(0);
}
// clear display
display.clearDisplay();
// display temperature
display.setTextSize(1);
display.setCursor(0,0);
display.print("Temperature: ");
display.setTextSize(2);
display.setCursor(0,17);
display.print(String(t,2));
display.print(" ");
display.setTextSize(1);
display.cp437(true);
display.write(167);
display.setTextSize(2);
display.print("C");
// display humidity
display.setTextSize(1);
display.setCursor(0, 35);
display.print("Humidity: ");
display.setTextSize(2);
display.setCursor(0, 45);
display.print(String(h,2));
display.print(" %");
display.display();
delay(2000);
}
上传代码至开发板,复位运行。
效果

动态演示

DS18B20传感器

详见:DS18B20模块 - 立创开源硬件平台 .
根据原理图可知,模块数据引脚对应 F5(0)引脚。
测试代码
#include "BLEDevice.h"
#include <DallasTemperature.h>
/* OLED */
#include <Wire.h>
#include <Adafruit_OLED_libraries/Adafruit_GFX.h>
#include <Adafruit_OLED_libraries/Adafruit_SSD1306.h>
#include <ArduinoJson.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define UART_SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
#define STRING_BUF_SIZE 100
// DS18B20 configuration
#define ONE_WIRE_BUS 0
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
BLEService UartService(UART_SERVICE_UUID);
BLECharacteristic Rx(CHARACTERISTIC_UUID_RX);
BLECharacteristic Tx(CHARACTERISTIC_UUID_TX);
BLEAdvertData advdata;
BLEAdvertData scndata;
bool notify = false;
void writeCB(BLECharacteristic* chr, uint8_t connID)
{
printf("Characteristic %s write by connection %d :\n", chr->getUUID().str(), connID);
if (chr->getDataLen() > 0) {
Serial.print("Received string: ");
Serial.print(chr->readString());
Serial.println();
}
}
void notifCB(BLECharacteristic* chr, uint8_t connID, uint16_t cccd)
{
if (cccd & GATT_CLIENT_CHAR_CONFIG_NOTIFY) {
printf("Notifications enabled on Characteristic %s for connection %d \n", chr->getUUID().str(), connID);
notify = true;
} else {
printf("Notifications disabled on Characteristic %s for connection %d \n", chr->getUUID().str(), connID);
notify = false;
}
}
void setup()
{
Serial.begin(115200);
advdata.addFlags();
advdata.addCompleteName("AMEBA_BLE_DEV");
scndata.addCompleteServices(BLEUUID(UART_SERVICE_UUID));
Rx.setWriteProperty(true);
Rx.setWritePermissions(GATT_PERM_WRITE);
Rx.setWriteCallback(writeCB);
Rx.setBufferLen(STRING_BUF_SIZE);
Tx.setReadProperty(true);
Tx.setReadPermissions(GATT_PERM_READ);
Tx.setNotifyProperty(true);
Tx.setCCCDCallback(notifCB);
Tx.setBufferLen(STRING_BUF_SIZE);
UartService.addCharacteristic(Rx);
UartService.addCharacteristic(Tx);
BLE.init();
BLE.configAdvert()->setAdvData(advdata);
BLE.configAdvert()->setScanRspData(scndata);
BLE.configServer(1);
BLE.addService(UartService);
BLE.beginPeripheral();
sensors.begin(); // 初始化传感器
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;)
; // Don't proceed, loop forever
}
delay(1000);
display.clearDisplay();
display.setTextColor(WHITE);
}
void loop()
{
sensors.requestTemperatures(); // 获取温度数据
float t = sensors.getTempCByIndex(0); // 获取第一个传感器的温度
if (isnan(t)) {
Serial.println("Failed to read from DS18B20 sensor!");
return;
}
// Create JSON payload
DynamicJsonDocument doc(256);
doc["temperature"] = String(t,2);
char json_string[256];
serializeJson(doc, json_string);
Serial.print("Publishing: ");
Serial.println(json_string);
String msg = (json_string);
Tx.writeString(msg);
if (BLE.connected(0) && notify) {
Tx.notify(0);
}
// clear display
display.clearDisplay();
// display temperature
display.setTextSize(1);
display.setCursor(0,0);
display.print("Temperature: ");
display.setTextSize(2);
display.setCursor(0,17);
display.print(String(t,2));
display.print(" ");
display.setTextSize(1);
display.cp437(true);
display.write(167);
display.setTextSize(2);
display.print("C");
display.display();
delay(1000);
}
上传代码至开发板,复位运行。
效果

同时使用 Bluefruit Connect 应用APP连接设备蓝牙,打开串口,即可获取温度信息

同时通过蓝牙 UART 转 MQTT,实现 Home Assistant 数据接收

具体方案见帖子:【BW21-CBV-Kit 开发套件测评】OLED蓝牙温湿度计与Home Assistant连接 .
锂电池供电
这里使用 200mAh 锂电池进行充放电测试

注意充电或供电需使用扩展板 Type-C 接口
充电
standby 指示灯熄灭,charge 指示灯常亮,充满则熄灭

放电
charge 指示灯熄灭,standby 指示灯常亮

无电池
charge 指示灯闪烁,standby 指示灯常亮

更新日志
🎨2025年6月18日更新
- 优化排针布局,增加排针与排母的间隔,使之与开发板更加匹配,且不影响杜邦线连接;
- 优化 3D 外壳沉头孔高度,使适配效果更佳;
- 优化 3D 外壳板载天线部分,移动侧边挖槽的位置,使布局更合理;
- 优化电路设计,增加 MOS 管充电防倒灌电路;
✨2025年6月19日更新
- 优化充电模块电路设计,底板增加 Type-C 电源充电接口,实现电池的充放电管理;
- 3D 外壳侧边挖槽位置优化,更新 3D 外壳附件;
🚀2025年6月23日更新
- 增加功能测试,包括 IIC OLED、SPI OLED显示、DHT11传感器测试、DS18B20测试、锂电池充放电测试等;
- 优化扩展板功能定义介绍内容、PCB效果显示内容等,提升浏览体验;
总结
本文介绍了安信可 BW21-CBV-Kit 开发套件扩展板及其外壳的项目设计,并根据板载资源进行了相关功能的测试,为后续 DIY 设计与项目开发作铺垫,也为该模组的快速开发和产品应用提供了参考。