如果你渴望快速踏入 HarmonyOS 设备开发的大门,小熊派 BearPi-HM Nano 绝对是你的最佳拍档。这块专为鸿蒙系统打造的 IoT 开发板,以其亲民的价格、丰富的例程和活跃的社区生态,吸引了无数开发者从零开始探索鸿蒙设备开发的奥秘。本文将带你从硬件认知出发,一步步完成环境搭建、编写代码、烧录运行,最终实现 WiFi 连接与 MQTT 通信的物联网实战项目。

一、开发板简介

1.1 硬件规格一览

BearPi-HM Nano 开发板外观尺寸为 73.2mm × 62.7mm × 11.1mm,板载资源相当丰富:

类别 规格详情
主控芯片 Hi3861RNIV100,160MHz 主频
存储资源 SRAM 352KB + ROM 288KB + Flash 2MB
无线通信 2.4GHz Wi-Fi (IEEE 802.11b/g/n),内置 PA/LNA
接口资源 15× GPIO、3× UART、2× SPI、2× I2C、7× ADC、6× PWM、1× SDIO
板载功能 NFC Tag (NT3H1101)、CH340 串口芯片、复位按键×1、用户按键×2、LED×2
扩展接口 E53 标准接口(兼容所有 E53 扩展板)
供电方式 USB Type-C (5V/1A)

核心芯片 Hi3861 是一款高度集成的 Wi-Fi SoC,封装为 QFN-32 (5mm×5mm),工作温度范围 -40℃ ~ +85℃。它内嵌完整的 IEEE 802.11b/g/n 基带和 RF 电路,支持 STA 和 AP 两种工作形态。作为 AP 时最大支持 6 个 STA 接入,足以满足小型物联网项目的组网需求。

1.2 板载资源解读

开发板采用三段式布局设计,各区域功能清晰:

  • 左侧区域:E53 扩展接口,预留了完整的 GPIO、SPI、I2C、ADC、UART 等引脚,方便连接各类传感器扩展板
  • 中间区域:Hi3861 主控芯片、NFC 线圈与芯片(实现 HarmonyOS "碰一碰"配网)、用户 LED 指示灯
  • 右侧区域:USB Type-C 接口(供电+烧录)、CH340 串口芯片、复位按键、两个用户自定义按键

实战心得:开发板出厂自带测试固件,上电后红色 LED 会闪烁,说明基础功能正常。如果 LED 不亮,首先检查 USB 线是否支持数据通信(部分充电线仅支持充电)。

1.3 硬件架构图

小熊派BearPi-HM Nano硬件架构图

二、开发环境搭建

2.1 推荐的纯 Windows 开发方案

2024 年以后,HUAWEI DevEco Device Tool 3.1+ 版本已支持纯 Windows 环境开发 Hi3861,大大简化了环境配置的复杂度。相比传统的 Linux 虚拟机方案,纯 Windows 开发有以下优势:

  • 编译速度更快(首次编译仅需 2 分 30 秒)
  • 烧录更便捷(一键烧录约 30 秒)
  • 环境配置步骤精简为三步:安装 DevEco Device Tool → 下载源码 → 配置工具链

环境配置步骤

  1. 下载安装 DevEco Device Tool:从华为开发者官网下载 devicetool-windows-tool-3.1.x.zip,解压后双击运行安装程序
  2. 获取 Hi3861 SDK:推荐通过 DevEco Marketplace 导入 hi3861_hdu_iot_application 项目,SDK 大小仅约 413MB
  3. 配置工具链:在 Project Settings → compiler_bin_path 中选择 DevTools_Hi3861V100_v1.0 文件夹

避坑提示

  • Windows 路径长度限制 260 字符,务必将项目放在磁盘根目录(如 D:\bearpi_project),否则编译会莫名其妙失败
  • 首次编译会自动下载 Python 依赖,建议配置国内 pip 镜像加速

2.2 硬件连接方式

小熊派开发板接线示意图

开发板通过 USB Type-C 线与电脑连接,即可完成供电、烧录、调试三大功能。连接时注意:

  • 使用数据线而非仅充电线(可通过观察是否能识别到 CH340 串口来判断)
  • 烧录时需要按住复位键再上电,或在烧录工具中选择重启进入烧录模式

三、入门示例:LED 闪烁与按键检测

3.1 LED 控制代码

LED 控制是最基础的入门实验。BearPi-HM Nano 板载一颗红色 LED,连接在 GPIO 9 上(低电平点亮)。以下是完整的 LED 闪烁代码:

#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_gpio.h"
#include "hi_gpio.h"
#include "hi_io.h"

#define LED_GPIO_PIN 9  // LED连接GPIO9

// GPIO初始化
static void GpioInit(void)
{
    // 初始化GPIO功能
    IoTGpioInit(LED_GPIO_PIN);
    // 设置GPIO为输出模式
    IoTGpioSetDir(LED_GPIO_PIN, IOT_GPIO_DIR_OUT);
}

// LED任务:控制LED以500ms间隔闪烁
static void LED_Task(void *arg)
{
    (void)arg;
    GpioInit();
    
    while (1) {
        // 输出低电平,点亮LED
        IoTGpioSetOutputVal(LED_GPIO_PIN, IOT_GPIO_VALUE0);
        osDelay(50);  // 500ms (10ms × 50)
        
        // 输出高电平,熄灭LED
        IoTGpioSetOutputVal(LED_GPIO_PIN, IOT_GPIO_VALUE1);
        osDelay(50);
    }
}

// 创建LED任务
static void LED_Entry(void)
{
    osThreadAttr_t attr = {
        .name = "LED_Task",
        .stack_size = 4096,
        .priority = osPriorityNormal,
    };
    
    if (osThreadNew(LED_Task, NULL, &attr) == NULL) {
        printf("[LED] Failed to create LED task!\r\n");
    }
}

// 使用SYS_RUN启动任务
SYS_RUN(LED_Entry);

实战要点

  • SYS_RUN() 宏会在系统启动时自动执行,无需手动调用 main()
  • osDelay() 的单位是 10ms,因此 osDelay(50) 表示 500ms
  • LED 电路采用低电平驱动设计(GPIO 输出低时电流从 VCC 经 LED 流向 GPIO)

3.2 按键检测代码

板载两个用户按键(KEY1 和 KEY2),可用于触发交互事件。按键电路采用中断方式设计:

#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_gpio.h"
#include "hi_gpio.h"

// 按键GPIO定义
#define KEY1_GPIO 5   // GPIO5 连接 KEY1
#define LED_GPIO 9   // GPIO9 连接 LED

static uint32_t key_press_count = 0;

// 按键中断回调函数
static void Key1_ISR(uint32_t gpioIrqNum)
{
    (void)gpioIrqNum;
    key_press_count++;
    printf("[KEY] Button pressed! Count: %u\r\n", key_press_count);
    
    // 翻转LED状态
    static uint8_t led_state = 0;
    led_state = !led_state;
    IoTGpioSetOutputVal(LED_GPIO, led_state ? IOT_GPIO_VALUE1 : IOT_GPIO_VALUE0);
}

// GPIO与中断初始化
static void GpioInterruptInit(void)
{
    // 初始化GPIO
    IoTGpioInit(KEY1_GPIO);
    IoTGpioInit(LED_GPIO);
    
    // 设置KEY1为输入模式
    IoTGpioSetDir(KEY1_GPIO, IOT_GPIO_DIR_IN);
    // 设置LED为输出模式
    IoTGpioSetDir(LED_GPIO, IOT_GPIO_DIR_OUT);
    
    // 配置GPIO5为下降沿触发中断(按键按下时触发)
    IoTGpioRegisterIsrFunc(KEY1_GPIO, IOT_INT_TYPE_EDGE, 
                            IOT_GPIO_EDGE_FALL_LEVEL_LOW, Key1_ISR, NULL);
}

static void KeyDemo_Entry(void)
{
    GpioInterruptInit();
    printf("[KEY Demo] Started. Press KEY1 to toggle LED.\r\n");
}

SYS_RUN(KeyDemo_Entry);

调试技巧:使用串口调试助手(MobaXterm 或 PuTTY)连接开发板,波特率设为 115200,按下按键时可在串口终端看到按键计数信息。

四、进阶示例:WiFi 连接与 MQTT 通信

4.1 WiFi STA 连接代码

WiFi 连接是物联网项目的基础能力。Hi3861 支持 STA(Station)模式连接到无线路由器:

#include "ohos_init.h"
#include "cmsis_os2.h"
#include "hi_wifi_api.h"
#include "lwip/netifapi.h"
#include "lwip/sockets.h"
#include <string.h>

// WiFi配置参数(修改为你的热点信息)
#define WIFI_SSID     "YourSSID"
#define WIFI_PASSWORD "YourPassword"

static volatile int g_connect_success = 0;
static struct netif *g_lwip_netif = NULL;

// WiFi连接状态回调
static void WifiConnectionHandler(uint8_t state, WifiLinkedInfo *info)
{
    (void)info;
    if (state == WIFI_STATE_CONNECTED) {
        printf("[WiFi] Connected to AP successfully!\r\n");
        g_connect_success = 1;
    } else {
        printf("[WiFi] Disconnected, state: %d\r\n", state);
    }
}

// WiFi扫描完成回调
static void WifiScanDoneHandler(uint8_t state, uint8_t size)
{
    (void)state;
    printf("[WiFi] Scan finished, found %d APs\r\n", size);
}

// WiFi初始化与连接
static int WiFiConnect(void)
{
    int ret;
    
    // 注册WiFi事件回调
   WifiEvent event = {
        .onWifiConnectionChanged = WifiConnectionHandler,
        .onWifiScanStateChanged = WifiScanDoneHandler,
    };
    ret = RegisterWifiEvent(&event);
    if (ret != WIFI_SUCCESS) {
        printf("[WiFi] Register event failed: %d\r\n", ret);
        return -1;
    }
    
    // 使能WiFi STA模式
    ret = EnableWifi();
    if (ret != WIFI_SUCCESS) {
        printf("[WiFi] Enable WiFi failed: %d\r\n", ret);
        return -1;
    }
    
    // 配置要连接的热点
    WifiDeviceConfig config = {0};
    strncpy(config.ssid, WIFI_SSID, sizeof(config.ssid) - 1);
    strncpy(config.password, WIFI_PASSWORD, sizeof(config.password) - 1);
    
    int networkId = -1;
    ret = AddDeviceConfig(&config, &networkId);
    if (ret != WIFI_SUCCESS || networkId < 0) {
        printf("[WiFi] Add config failed: %d, networkId: %d\r\n", ret, networkId);
        return -1;
    }
    
    // 连接到热点
    ret = ConnectTo(networkId);
    if (ret != WIFI_SUCCESS) {
        printf("[WiFi] Connect failed: %d\r\n", ret);
        return -1;
    }
    
    // 等待连接成功(最多等待15秒)
    int wait_count = 0;
    while (!g_connect_success && wait_count < 150) {
        usleep(100000);  // 100ms
        wait_count++;
    }
    
    if (!g_connect_success) {
        printf("[WiFi] Connection timeout!\r\n");
        return -1;
    }
    
    // 获取网络接口并启动DHCP
    g_lwip_netif = netifapi_netif_find("wlan0");
    if (g_lwip_netif == NULL) {
        printf("[WiFi] Netif not found!\r\n");
        return -1;
    }
    
    ret = netifapi_netif_set_default(g_lwip_netif);
    ret = dhcp_start(g_lwip_netif);
    
    // 等待DHCP完成
    printf("[WiFi] Waiting for DHCP...\r\n");
    for (int i = 0; i < 30; i++) {
        osDelay(20);
        if (dhcp_is_bound(g_lwip_netif) == ERR_OK) {
            printf("[WiFi] DHCP OK!\r\n");
            // 打印获取的IP信息
            ip4_addr_t ip, gw, mask;
            netifapi_netif_get_addr(g_lwip_netif, &ip, &netmask, &gw);
            printf("[WiFi] IP: %s\r\n", ip4addr_ntoa(&ip));
            return 0;
        }
    }
    
    printf("[WiFi] DHCP timeout!\r\n");
    return -1;
}

static void WiFiTask(void *arg)
{
    (void)arg;
    
    if (WiFiConnect() == 0) {
        printf("[WiFi] Ready for network operations!\r\n");
        // 这里可以开始TCP/UDP/MQTT通信
    } else {
        printf("[WiFi] Connection failed!\r\n");
    }
}

static void WiFiDemo_Entry(void)
{
    osThreadAttr_t attr = {
        .name = "WiFi_Task",
        .stack_size = 4096,
        .priority = osPriorityNormal,
    };
    osThreadNew(WiFiTask, NULL, &attr);
}

SYS_RUN(WiFiDemo_Entry);

4.2 MQTT 客户端实现

MQTT 是物联网领域最流行的消息传输协议。以下代码演示如何在 Hi3861 上实现 MQTT 客户端:

#include "ohos_init.h"
#include "cmsis_os2.h"
#include "MQTTClient.h"
#include "lwip/sockets.h"

// MQTT服务器配置
#define MQTT_SERVER_IP   "broker.emqx.io"  // 公共MQTT服务器
#define MQTT_SERVER_PORT 1883
#define MQTT_CLIENT_ID   "BearPi_Hi3861"
#define MQTT_USERNAME     "demo"
#define MQTT_PASSWORD     "demo"
#define MQTT_TOPIC_PUB    "bearpi/temperature"
#define MQTT_TOPIC_SUB    "bearpi/control"

static MQTTClient mqtt_client;
static Network mqtt_network;
static unsigned char mqtt_send_buf[1024];
static unsigned char mqtt_recv_buf[1024];

// MQTT消息回调
static void mqtt_message_arrived(MessageData *msg_data)
{
    if (msg_data == NULL) return;
    
    printf("[MQTT] Received from topic '%.*s': %.*s\r\n",
           msg_data->topicName->lenstring.len,
           msg_data->topicName->lenstring.data,
           msg_data->message->payloadlen,
           (char *)msg_data->message->payload);
}

// MQTT连接与通信
static void MqttTask(void)
{
    int ret;
    
    // 初始化网络连接
    NetworkInit(&mqtt_network);
    ret = NetworkConnect(&mqtt_network, MQTT_SERVER_IP, MQTT_SERVER_PORT);
    if (ret != 0) {
        printf("[MQTT] Network connect failed: %d\r\n", ret);
        return;
    }
    printf("[MQTT] Network connected to %s:%d\r\n", MQTT_SERVER_IP, MQTT_SERVER_PORT);
    
    // 初始化MQTT客户端
    MQTTClientInit(&mqtt_client, &mqtt_network, 3000,
                   mqtt_send_buf, sizeof(mqtt_send_buf),
                   mqtt_recv_buf, sizeof(mqtt_recv_buf));
    
    // 配置连接参数
    MQTTPacket_connectData connectData = MQTTPacket_connectData_initializer;
    connectData.keepAliveInterval = 30;
    connectData.cleansession = 1;
    connectData.clientID.cstring = MQTT_CLIENT_ID;
    connectData.username.cstring = MQTT_USERNAME;
    connectData.password.cstring = MQTT_PASSWORD;
    
    // 连接MQTT服务器
    ret = MQTTConnect(&mqtt_client, &connectData);
    if (ret != SUCCESS) {
        printf("[MQTT] Connect failed: %d\r\n", ret);
        NetworkDisconnect(&mqtt_network);
        return;
    }
    printf("[MQTT] Connected to broker!\r\n");
    
    // 订阅主题
    ret = MQTTSubscribe(&mqtt_client, MQTT_TOPIC_SUB, QOS1, mqtt_message_arrived);
    if (ret != SUCCESS) {
        printf("[MQTT] Subscribe failed: %d\r\n", ret);
    } else {
        printf("[MQTT] Subscribed to: %s\r\n", MQTT_TOPIC_SUB);
    }
    
    // 循环发布消息
    int msg_count = 0;
    while (1) {
        // 模拟读取传感器数据(实际项目中替换为真实传感器读取)
        float temperature = 25.0f + (msg_count % 20) * 0.5f;
        char payload[64];
        snprintf(payload, sizeof(payload), 
                 "{\"device\":\"%s\",\"temp\":%.1f,\"humidity\":%.1f}",
                 MQTT_CLIENT_ID, temperature, 60.0f + msg_count % 30);
        
        MQTTMessage message = {
            .qos = QOS1,
            .retained = 0,
            .payload = payload,
            .payloadlen = strlen(payload),
        };
        
        ret = MQTTPublish(&mqtt_client, MQTT_TOPIC_PUB, &message);
        if (ret == SUCCESS) {
            printf("[MQTT] Published to %s: %s\r\n", MQTT_TOPIC_PUB, payload);
        }
        
        osDelay(200);  // 每2秒发布一次
        msg_count++;
    }
}

static void MqttDemo_Entry(void)
{
    osThreadAttr_t attr = {
        .name = "MQTT_Task",
        .stack_size = 8192,  // MQTT任务需要更大的栈空间
        .priority = osPriorityNormal,
    };
    osThreadNew(MqttTask, NULL, &attr);
}

SYS_RUN(MqttDemo_Entry);

MQTT 测试工具:开发电脑端可使用 MQTTX 或 Paho MQTT 客户端连接同一服务器,订阅 bearpi/temperature 主题即可看到设备上报的数据,向 bearpi/control 主题发送消息可控制设备行为。

五、外设操作速查表

5.1 常用 GPIO 引脚定义

功能 GPIO 说明
板载 LED GPIO_9 低电平点亮
用户按键 KEY1 GPIO_5 按下为低电平
用户按键 KEY2 GPIO_8 按下为低电平
E53 接口 SCL GPIO_13 I2C0 时钟
E53 接口 SDA GPIO_14 I2C0 数据
UART0 TX GPIO_11 调试串口发送
UART0 RX GPIO_12 调试串口接收

5.2 I2C 传感器读取示例

以读取光照强度传感器 BH1750 为例:

#include "hi_i2c.h"

#define BH1750_ADDR     0x23  // BH1750 I2C地址
#define BH1750_POWER_ON 0x01  // 开启测量命令
#define BH1750_CON_H    0x10  // 连续高分辨率模式

// I2C初始化
static void I2C_Init(void)
{
    hi_i2c_idx_id i2c_id = HI_I2C_IDX_0;
    hi_u32 baudrate = 400000;  // 400KHz 高速模式
    
    hi_i2c_init(i2c_id, baudrate);
    hi_i2c_set_baudrate(i2c_id, baudrate);
}

// 读取光照强度(单位:lux)
static int BH1750_ReadLux(float *lux)
{
    hi_i2c_idx_id i2c_id = HI_I2C_IDX_0;
    hi_u8 send_data[1] = {BH1750_CON_H};
    hi_u8 recv_data[2] = {0};
    
    // 发送测量命令
    if (hi_i2c_write(i2c_id, BH1750_ADDR, send_data, 1) != HI_ERR_SUCCESS) {
        return -1;
    }
    
    // 等待测量完成(120ms)
    usleep(150000);
    
    // 读取两字节数据
    if (hi_i2c_read(i2c_addr, BH1750_ADDR, NULL, recv_data, 2) != HI_ERR_SUCCESS) {
        return -1;
    }
    
    // 计算光照强度
    // BH1750 分辨率 1 lux, 高位在前
    hi_u16 raw = (recv_data[0] << 8) | recv_data[1];
    *lux = raw / 1.2f;
    
    return 0;
}

六、常见问题与踩坑记录

问题 1:编译报错 “toolchain not found”

解决方案:检查 DevEco Device Tool 中 compiler_bin_path 是否正确配置为 DevTools_Hi3861V100_v1.0 文件夹路径。

问题 2:烧录失败,提示 “Failed to connect device”

解决方案:确保开发板进入烧录模式——按住复位键,上电后保持 2 秒,串口应打印 ###HiBurn### 提示。

问题 3:WiFi 连接成功但无法获取 IP

解决方案:确认路由器开启了 DHCP 功能,或者手动设置静态 IP:

netifapi_netif_set_addr(g_lwip_netif, &ipaddr, &netmask, &gw);

问题 4:MQTT 连接服务器失败

解决方案

  1. 确认 WiFi 已成功连接并获取 IP
  2. 检查 MQTT 服务器地址和端口是否正确
  3. 部分企业网络需要配置代理或使用 WebSocket 方式连接

七、总结

小熊派 BearPi-HM Nano 开发板是入门 HarmonyOS 设备开发的绝佳选择。它麻雀虽小五脏俱全——Hi3861 芯片虽然资源有限,但 Wi-Fi、NFC、GPIO、I2C、SPI、UART 等物联网核心能力一应俱全,配合丰富的 E53 扩展板和海量的社区例程,完全可以支撑从 LED 控制到云端对接的完整项目。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐