鸿蒙 OS IoT 开发之基础 —— 软总线(遇阻)
完成前面几个标准硬件的驱动还算顺利,到了我最关心的“软总线”,却遇到了麻烦。
在 Hi3861 端,虽然所安装的 OpenHarmony 3.0.3 版本中,包含了 softbus_lite(即轻量版软总线),虽说功能少于标准软总线,但我想既然存在,就应该能用,毕竟不论是 OS,还是Hi3861,都是原生的。
因为目前只是想验证一下软总线的连接效果,还没有开始深入学习鸿蒙OS系统,更没有开始学习华为的 ArkTS 语言,所以只能求助于 AI 。
先是让豆包帮忙编写 HI3861 上的软总线程序,它编写的煞有介事,可由于不能完整读取我安装的 OS 相关资源,总是出现因版本不对应导致的函数格式错误,编译通过都很困难。
小艺也尝试了一下,效果也不行,因为我本身就对鸿蒙OS下的 HI3861 编程生疏,无法像以前让 AI 辅助编写我熟悉的 STM32 程序那样,一些bug我直接就改了。
尝试将线上软总线相关的 API 说明打印成文档, 尝试发给 Kimi,它似乎消化的文档,编出的程序基本无错。但进一步提问则遇到的需要订阅才能得到回答的尴尬。现在还没有持续的需求,故只好放弃。
再回头求助豆包,跌跌绊绊总是搞定了 hi3861 端的程序:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include "hi_types_base.h"
#include "hi_wifi_api.h"
#include "hi_io.h"
#include "hi_gpio.h"
#include "lwip/ip_addr.h"
#include "lwip/netifapi.h"
#include "los_task.h"
#include "ohos_init.h"
#include "ssd1306_i2c.h"
#include "discovery_service.h" /* PublishService, StartDiscovery */
#include "session.h"
#define LED_INTERVAL_TIME_MS 1000
#define LED_TEST_GPIO HI_GPIO_IDX_9
#define LED_TEST_GPIO_NAME HI_IO_NAME_GPIO_9
#define WIFI_SSID "www.embedream.com_2.4G"
#define WIFI_PASSWD "embedream"
#define WIFI_HOSTNAME "Hi3861_01"
#define APP_INIT_VAP_NUM 2
#define APP_INIT_USR_NUM 2
static struct netif *g_lwip_netif = NULL;
static hi_u32 g_wifi_task_id = 0;
#define MODULE_NAME "DEV_L0"
#define CAPABILITY "ddmpCapability"
#define SESSION_NAME "com.harmony.test.session"//"demo_session"
#define MAX_RECV_BUF_LEN 128
static uint8_t dummy_buf[2] = {0x00,0x00}; // static,生命周期全局
/* ========== 新增全局状态:防止重复初始化软总线 ========== */
typedef struct {
hi_bool isServicePublished;
hi_bool isSessionServerCreated;
int publishIdUsed;
} SoftBusStatus;
static SoftBusStatus g_sbStatus = {HI_FALSE, HI_FALSE, 1}; // publishIdUsed可以随便指定一个非0值,后续PublishService会返回实际使用的publishId
static int g_sessionId = -1;
/**
* @brief 设置DHCP Hostname(路由器后台显示的设备名)
*/
static void Wifi_SetHostName(const char *name)
{
err_t ret;
if(g_lwip_netif == NULL || name == NULL)
{
printf("Wifi_SetHostName: netif or name is NULL\r\n");
return;
}
ret = netifapi_set_hostname(g_lwip_netif, name, strlen(name));
printf("✅ Hostname set:\"%s\", ret=%d\r\n", name, ret);
}
static void hi_sta_reset_addr(struct netif *pst_lwip_netif)
{
ip4_addr_t st_gw;
ip4_addr_t st_ipaddr;
ip4_addr_t st_netmask;
if (pst_lwip_netif == NULL) {
printf("hisi_reset_addr::Null param of netdev\r\n");
return;
}
IP4_ADDR(&st_gw, 0, 0, 0, 0);
IP4_ADDR(&st_ipaddr, 0, 0, 0, 0);
IP4_ADDR(&st_netmask, 0, 0, 0, 0);
netifapi_netif_set_addr(pst_lwip_netif, &st_ipaddr, &st_netmask, &st_gw);
}
static void wifi_wpa_event_cb(const hi_wifi_event *hisi_event)
{
if (hisi_event == NULL)
return;
switch (hisi_event->event) {
case HI_WIFI_EVT_SCAN_DONE:
printf("WiFi: Scan results available\n");
break;
case HI_WIFI_EVT_CONNECTED:
printf("WiFi: ✅ AP Connected, start DHCP\n");
if(g_lwip_netif != NULL)
{
netifapi_dhcp_start(g_lwip_netif);
}
break;
case HI_WIFI_EVT_DISCONNECTED:
printf("WiFi: ❌ Disconnected\n");
/* wifi断开,复位软总线状态标记,下次重连可以重新初始化 */
g_sbStatus.isServicePublished = HI_FALSE;
g_sbStatus.isSessionServerCreated = HI_FALSE;
g_sessionId = -1;
if(g_lwip_netif != NULL)
{
netifapi_dhcp_stop(g_lwip_netif);
hi_sta_reset_addr(g_lwip_netif);
}
Ssd1306_Clear();
Ssd1306_DisplayString(1, 0, "WiFi Disconnect");
break;
case HI_WIFI_EVT_WPS_TIMEOUT:
printf("WiFi: wps is timeout\n");
break;
default:
break;
}
}
static int hi_wifi_my_connect(void)
{
int ret;
errno_t rc;
hi_wifi_assoc_request assoc_req = {0};
rc = memcpy_s(assoc_req.ssid, HI_WIFI_MAX_SSID_LEN + 1, WIFI_SSID, strlen(WIFI_SSID));
if (rc != EOK) {
printf("memcpy ssid fail\n");
return -1;
}
assoc_req.auth = HI_WIFI_SECURITY_WPA2PSK;
rc = memcpy_s(assoc_req.key, HI_WIFI_MAX_KEY_LEN + 1, WIFI_PASSWD, strlen(WIFI_PASSWD));
if (rc != EOK) {
printf("memcpy key fail\n");
return -1;
}
ret = hi_wifi_sta_connect(&assoc_req);
if (ret != HISI_OK) {
printf("hi_wifi_sta_connect fail ret=%d\n", ret);
return -1;
}
printf("hi_wifi_sta_connect success\n");
return 0;
}
// ================= SoftBus 发现服务回调 =================
static void OnPublishSuccess(int publishId)
{
printf("[SoftBus] Publish success, publishId = %d\n", publishId);
g_sbStatus.isServicePublished = HI_TRUE;
}
static void OnPublishFail(int publishId, PublishFailReason reason)
{
printf("[SoftBus] Publish failed, publishId = %d, reason = %d\n", publishId, reason);
g_sbStatus.isServicePublished = HI_FALSE;
}
static struct IPublishCallback pubCb = {
.onPublishSuccess = OnPublishSuccess,
.onPublishFail = OnPublishFail
};
// ================= SoftBus Session 回调 =================
static int OnSessionOpened(int sessionId)
{
char peerName[64] = {0};
GetPeerSessionName(sessionId, peerName, sizeof(peerName));
printf("[Session] Opened, sessionId = %d, peer = %s\n", sessionId, peerName);
g_sessionId = sessionId;
Ssd1306_Clear();
Ssd1306_DisplayString(1,0,"Mobile Connected");
return 0;
}
static void OnSessionClosed(int sessionId)
{
printf("[Session] Closed, sessionId = %d\n", sessionId);
if (g_sessionId == sessionId) {
g_sessionId = -1;
}
Ssd1306_Clear();
Ssd1306_DisplayString(1,0,"Session Closed");
}
static void OnBytesReceived(int sessionId, const void *data, unsigned int dataLen)
{
if (data == NULL || dataLen == 0 || dataLen >= MAX_RECV_BUF_LEN) {
printf("[Session] recv invalid len:%u\n", dataLen);
return;
}
printf("[Session] Received %u bytes on session %d: %.*s\n",
dataLen, sessionId, dataLen, (const char *)data);
// 回显数据
if (g_sessionId == sessionId) {
int ret = SendBytes(sessionId, (const unsigned char *)data, dataLen);
if(ret != 0) {
printf("[Session] SendBytes ret=%d\n", ret);
}
}
}
static struct ISessionListener listener = {
.onSessionOpened = OnSessionOpened,
.onSessionClosed = OnSessionClosed,
.onBytesReceived = OnBytesReceived
};
/**
* @brief 释放软总线资源:取消发布、销毁会话服务
*/
static void SoftBusCleanup(void)
{
int ret;
if(g_sbStatus.isServicePublished) {
ret = UnPublishService(MODULE_NAME, g_sbStatus.publishIdUsed);
printf("[SoftBus] UnPublishService ret=%d\n", ret);
g_sbStatus.isServicePublished = HI_FALSE;
}
if(g_sbStatus.isSessionServerCreated) {
ret = DestroySessionServer(MODULE_NAME, SESSION_NAME);
printf("[SoftBus] DestroySessionServer ret=%d\n", ret);
g_sbStatus.isSessionServerCreated = HI_FALSE;
}
g_sessionId = -1;
}
/* WiFi拿到有效IP之后执行软总线初始化 */
void SoftBusStart(void)
{
int ret;
uint32_t probeCnt = 0;
if(g_sbStatus.isServicePublished && g_sbStatus.isSessionServerCreated) {
printf("[SoftBus] already init, skip SoftBusStart\n");
return;
}
LOS_Msleep(300);
CommonDeviceInfo devInfo[2];
devInfo[0].key = COMM_DEVICE_KEY_DEVTYPE;
devInfo[0].value = "L0";
devInfo[1].key = COMM_DEVICE_KEY_DEVNAME;
devInfo[1].value = MODULE_NAME;
PublishInfo pubInfo = {0};
pubInfo.publishId = g_sbStatus.publishIdUsed;
//pubInfo.mode = DISCOVER_MODE_ACTIVE; // 使用主动广播模式
pubInfo.mode = DISCOVER_MODE_PASSIVE; // 被动模式(Lite 设备仅支持被动)
pubInfo.medium = COAP;
pubInfo.freq = MID;
pubInfo.capability = CAPABILITY;
pubInfo.capabilityData = dummy_buf;
pubInfo.dataLen = sizeof(dummy_buf);
printf("[DEBUG] CAPABILITY=\"%s\", publishId=%d\n",pubInfo.capability, pubInfo.publishId);
ret = PublishService(MODULE_NAME, &pubInfo, &pubCb);
if (ret != 0) {
printf("[SoftBus][ERR] PublishService failed, ret=%d\n", ret);
Ssd1306_Clear();
Ssd1306_DisplayString(1, 4, "Publish FAIL");
return;
}
printf("[SoftBus] PublishService invoke OK\n");
LOS_Msleep(300);
ret = SetCommonDeviceInfo(devInfo, 2);
if(ret == 0)
{
printf("[SoftBus] Post‑Publish SetCommonDeviceInfo OK\n");
}
else
{
printf("[SoftBus][WARN] Post‑Publish SetCommonDeviceInfo still fail, ignore\n");
}
ret = CreateSessionServer(MODULE_NAME, SESSION_NAME, &listener);
if (ret != 0) {
printf("[SoftBus][ERR] CreateSessionServer failed, ret=%d\n", ret);
Ssd1306_Clear();
Ssd1306_DisplayString(1, 4, "CreateSess FAIL");
return;
}
g_sbStatus.isSessionServerCreated = HI_TRUE;
printf("[SoftBus] ✅ Session Server created successfully.\n");
Ssd1306_Clear();
Ssd1306_DisplayString(1, 4, "Session Server OK");
}
/* WiFi业务任务 */
static void WifiStaTask(void)
{
int ret;
char ifname[WIFI_IFNAME_MAX_SIZE + 1] = {0};
int len = sizeof(ifname);
ip4_addr_t ip,mask,gw;
err_t err;
int getted_ip = 0;
LOS_Msleep(1000);
/* 注意:上电首次执行deinit会返回失败,属于正常现象 */
ret = hi_wifi_deinit();
if (ret != HISI_OK) {
printf("hi_wifi_deinit: first run may fail, ret=%d\n", ret);
}
LOS_Msleep(100);
ret = hi_wifi_init(APP_INIT_VAP_NUM, APP_INIT_USR_NUM);
if (ret != HISI_OK) {
printf("hi_wifi_init fail ret=%d\n", ret);
return;
}
printf("hi_wifi_init success\n");
ret = hi_wifi_sta_start(ifname, &len);
if (ret != HISI_OK) {
printf("hi_wifi_sta_start fail ret=%d\n", ret);
return;
}
printf("hi_wifi_sta_start success, ifname = %s\n", ifname);
ret = hi_wifi_register_event_callback(wifi_wpa_event_cb);
if (ret != HISI_OK) {
printf("register wifi event callback failed ret=%d\n", ret);
} else {
printf("register wifi event callback success\n");
}
g_lwip_netif = netifapi_netif_find(ifname);
if (g_lwip_netif == NULL) {
printf("get netif failed\n");
return;
}
printf("get netif success\n");
Wifi_SetHostName(WIFI_HOSTNAME);
hi_wifi_my_connect();
Ssd1306_DisplayString(1, 1, "Wifi Connecting");
while(1)
{
hi_gpio_set_ouput_val(LED_TEST_GPIO, HI_GPIO_VALUE0);
LOS_Msleep(LED_INTERVAL_TIME_MS);
hi_gpio_set_ouput_val(LED_TEST_GPIO, HI_GPIO_VALUE1);
LOS_Msleep(LED_INTERVAL_TIME_MS);
if(getted_ip == 0)
{
err = netifapi_netif_get_addr(g_lwip_netif, &ip, &mask, &gw);
if(err == ERR_OK && !ip4_addr_isany(&ip))
{
printf("✅ Board IP: %s\n", ip4addr_ntoa(&ip));
getted_ip = 1;
Ssd1306_DisplayString(1, 2, "Wifi connected");
SoftBusStart();
}
}
}
}
void WifiDemoEntry(void)
{
hi_u32 ret;
ret = hi_gpio_init();
if (ret != HI_ERR_SUCCESS) {
printf("===== ERROR ===== gpio -> hi_gpio_init ret:%d\r\n", ret);
return;
}
printf("----- gpio init success-----\r\n");
ret = hi_io_set_func(LED_TEST_GPIO_NAME, HI_IO_FUNC_GPIO_9_GPIO);
if (ret != HI_ERR_SUCCESS) {
printf("===== ERROR ===== gpio -> hi_io_set_func ret:%d\r\n", ret);
return;
}
printf("----- io set func success-----\r\n");
hi_gpio_set_dir(LED_TEST_GPIO, HI_GPIO_DIR_OUT);
hi_gpio_set_ouput_val(LED_TEST_GPIO, HI_GPIO_VALUE1);
Ssd1306_Init();
Ssd1306_Clear();
Ssd1306_DisplayString(1, 0, "Hello Hi3861");
TSK_INIT_PARAM_S taskParam = {0};
taskParam.pfnTaskEntry = (TSK_ENTRY_FUNC)WifiStaTask;
taskParam.uwStackSize = 8192;
taskParam.pcName = "WifiStaTask";
taskParam.usTaskPrio = 6;
LOS_TaskCreate(&g_wifi_task_id, &taskParam);
}
SYS_RUN(WifiDemoEntry);
运行显示是完成了 publishiServer。
开始着手编写手机端。
考虑到鸿蒙手机端编程,小艺应该胜于豆包,故选择了小艺。
也确实感觉它回复的很专业,说得头头是道。
因为是第一次使用 DevEco Studio,在它的指导下,重新安装了支持鸿蒙 7.0 的最新版本 DevEco studio ,顺利完成了项目创建,成功运行了 Hello world 程序。
当初也安装了 Android Studio ,可那时没有 AI ,所以没有搞定标准的手机编程,只能用 Processing 凑合编写了一些手机上的测试用程序。
这次很顺利的实现了标准的手机编程过程,看来以后可以基于鸿蒙手机做些和单片机配合的应用了。
由于小艺开始的好表现,充满期望的将编写测试 Hi3861 测试程序的任务交给了它。
我详细叙述了需求,并把 OpenHarmony的版本,软总线的版本,以及hi3861的程序都给了它,它也一本正经的给出了结果,并且详细说明了应该怎样做?为何?相当有理有据。
可真正落实到 DevEco 环境的项目上,麻烦不断,总是有些语法和函数不对。连续多次修改,消除一个bug,会引出更多的bug,因没有这方面的能力,所以只能听它的,最后失去了耐心,放弃。
发现 DevEco Studio 中也有一个 AI :CodeGenie
我想它是附在 DevEco 中的,应该更专业。故尝试将此任务交予它。
它也确实完成了,错误比小艺少很多,虽说 bug 缩小到三个,可我还是无法借助 AI 彻底消除,看来基本功不扎实靠 AI 还是不行。
在来回折腾的过程中,我发现有一些官方的文档:
文档中心
https://developer.huawei.com/consumer/cn/doc/doccenter-capabilities/distributedservice-kit-intro
只是对于轻量版的软总线说明较少。
还在文档中发现了相关的示例程序:
感觉和我要实现的功能很接近,故将其迁移(以我已能运行的 hello world 程序为基础)到我的测试程序中,之所以说是迁移,因为直接克隆,和我的 DevEco 版本不配,只好将核心程序对应替换我已经能运行的程序中对应部分。
程序倒是能运行了,可无法和 Hi3861 建立连接。
到此只好放弃,等进一步学习后再尝试吧。
折腾了这么久,还是有点收获的:
其一:基本熟悉了 HI3861 的开发
其二:大致知道了鸿蒙系统手机端的开发,基本学会了 DevEco Studio 的使用。
在这个过程中,明显感受到了鸿蒙系统及 Hi3861 开发支持的 ToB 特性,至少目前它不像乐鑫的 ESP32,对个人开发不是太友好。估计还在成长期,顾不上个人,重点将精力放在企业用户上,尽快完善鸿蒙生态。
看来我还是先缓一下,一方面学习鸿蒙相关知识,一方面等官方的服务完善,现在版本变化太快,所以 AI 也难以应对。或者等 ESP32 这类大众芯片能支持鸿蒙,这样就可以得到一个相对完整、可直接运行的例程,在例程的基础上添加自己的功能就方便多了。
目前我还是花点精力完善一下我的小车相关内容,想尝试将小车编程学习和 AI 接轨,从而使我费心费力开模做的小车模型能物尽其用。
————————————————
更多推荐


所有评论(0)